数据结构与算法之栈

  前言:我也是即学即写的,难免有错误。欢迎之处这篇博文的不足之处。

一、什么是栈?

  栈是数据结构的一种,他的原理就是后进先出(先进后出是一样的道理),如图(无图言屌)

  

  看到没,你如果把栈当成一个开口向上的容器的话,你最先存储的a1要想取出来,必须把上面的元素都取出来,而你最后一个存储的a5元素却可以第一个取出来。这就是后进先出。

PS:我这里的top是指向栈顶元素的,这里用到的top初值是-1.有的书中(好像是严蔚敏的)的top的初始值是0.所以top指向栈顶元素的上一个存储单元。我个人觉得还是top初始值为-1比较好。关于这个知识点下面详讲。

二、为什么要学栈?学了有什么用?

   栈是解决问题的一种方法,有些情况下用栈是非常实用的,举一些例子,比如你浏览网页,当你打开浏览器的开始,第一个肯定是首页,然后你搜索了蜀云泉,第二个肯定是搜索列表,然后你点击进入蜀云泉的博客,好了,蜀云泉的博客界面是你的第三个界面对吧。现在你点击一下浏览器的返回键,你可以看看浏览器返回的是哪个界面?没错是第二个搜索界面。这其中就用到了栈的思想。先进后出或者后进先出。

  再举个例子,方法里面调用方法,我写个代码瞧瞧:

 1  bool then(int item)
 2 {
 3      if (item > 0)
 4      {
 5          return true;
 6      }
 7      else
 8          return false;
 9 }
10 
11  void start()
12  {
13 
14      Console.WriteLine(then(5));
15                
16 }

来看start方法里面,先进入的是start方法,后进入的是then方法,但是是then方法先返回值,这还是后进先出的道理。

三、栈怎么用代码去实现?

  终于到代码阶段了,栈分为顺序栈和链栈(是不是与顺序表,链表很相似?答案是相似度很高啊...)

首先来看看BCL中的栈的基本操作:BCL就是visual studio 里面封装的栈Stack。直接拿来用就好。

 1  class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5 
 6             Stack<int> stack = new Stack<int>();   //这是定义一个栈的对象stack
 7             stack.Push(6);                         //Push就是入栈的操作,它是没有返回值的,含一个参数
 8             stack.Pop();                           //Pop有返回值,返回栈顶的元素并且把栈顶元素删掉,top指向下一个元素
 9             stack.Peek();                //Peek有返回值,返回栈顶元素。与Pop的区别在于Peek不删除栈顶元素
10             stack.Count();                         //有返回值,返回栈里面的元素个数
11             stack.Clear();                         //清空栈
12       
13         }
14     }


这几个是比较常见的,还有一些其他的就不多叙述了。看到这里你有可能会说既然visual studio已经封装好栈了我直接用不就得了,那我不是已经学会栈了吗,真简单。是的,你现在已经学会栈这个数据结构了,但是算法呢?Pop,Peek它们内部是怎么实现的你知道吗?如果在某个偶然的条件下,BCL类不能用了,怎么办?所以要学这个算法,至少你可以自己写一个栈,用自己的代码,让BCL凉快去吧。(了解内部的构成后,其实用BCL也没什么不对。人家visual studio封装好的不用白不用...)

顺序栈:

首先新建一个接口:

1 interface IStack<T>        //这里的T是泛型的意思,就是你定义成什么类型T就是什么类型,相当于T是类型的一个代表
2     {
3         int Count{get; }
4         bool IsEmpty();
5         int GetLength();
6         void Push(T item);
7         T Pop();
8         T Peek();
9     }

然后新建一个类作为顺序栈,这个类继承接口:

 1 namespace ConsoleApplication2
 2 {
 3     class MyStack <T>:IStack<T>
 4     {
 5         private T [] Data;
 6         private int top;
 7 
 8         public MyStack(int size)
 9         {
10             Data = new T[size];
11             top = -1;
12         }
13         public MyStack():this(10)
14         {
15             top = -1;
16         }
17 
18 
19 
20         public int Count
21         {
22             get { return top + 1; }
23         }
24 
25         public bool IsEmpty()
26         {
27             return Count == 0;
28         }
29 
30         public int GetLength()
31         {
32             return Count;
33         }
34 
35         public void Push(T item)
36         {
37             if (Count==Data.Length)
38             {
39                 Console.WriteLine("栈已经满了,无法插入元素");
40             }
41             else
42             {
43                 Data[top + 1] = item;
44                 top++;
45             }
46             
47         }
48 
49         public T Pop()
50         {
51             if (Count>0)
52             {
53                 T item = Data[top];
54                 top--;
55                 return item;
56             }
57             else
58             {
59                 Console.WriteLine("栈是空的,无法取值");
60                 return default(T);
61             }
62             
63         }
64 
65         public T Peek()
66         {
67             if (Count>0)
68             {
69                 return Data[top];
70             }
71             else
72             {
73                 Console.WriteLine("栈是空的,无法取值");
74                 return default(T);
75             }
76         }
77     }
78 }

完成了。在控制台里面调用一下看看行不行

 1  class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5   
 6             //顺序栈     
 7            IStack<int> stack = new MyStack<int>();
 8          
 9             stack.Push(3);
10             stack.Push(8);
11             Console.WriteLine(stack.Count);
12             Console.WriteLine(stack.Peek());
13             Console.WriteLine(stack.Count);
14             Console.WriteLine(stack.Pop());
15             Console.WriteLine(stack.Count);
16             Console.ReadLine();
17           
18         }
19     }

运行结果:

链栈:

新建一个类作为链栈的节点:

 1  class Node<T>
 2     {
 3         private T data;
 4         public T Data
 5         {
 6             get { return data; }
 7             set { data = value; }
 8         }
 9 
10         private Node<T> next;
11         internal Node<T> Next
12         {
13             get { return next; }
14             set { next = value; }
15         }
16         
17         public Node()
18         {
19             data = default(T);
20             next = null;
21         }
22         public Node(T data)
23         {
24             this.data = data;
25             next = null;
26         }
27         public Node(T data, Node<T> next)
28         {
29             this.data = data;
30             this.next = next;
31         }
32         public Node(Node<T> next)
33         {
34             this.next = next;
35             data = default(T);
36         }
37 
38 
39     }

新建一个类作为链栈功能的实现,还是继承那个接口:

 1 namespace ConsoleApplication2
 2 {
 3     class ListStack<T>:IStack<T>
 4     {
 5         private Node<T> top;
 6         private int count;
 7 
 8         public int Count
 9         {
10             get { return count; }
11         }
12 
13         public bool IsEmpty()
14         {
15             return count == 0;
16         }
17 
18         public int GetLength()
19         {
20             return count;
21         }
22 
23         public void Push(T item)
24         {          
25             Node<T> newNode = new Node<T>(item);
26             newNode.Next = top;
27             top = newNode;
28             count++;
29         }
30 
31         public T Pop()
32         {
33             if (count>0)
34             {
35                 T data = top.Data;
36                 top = top.Next;
37                 count--;
38                 return data;
39             }
40             else
41             {
42                 Console.WriteLine("栈是空的,无法取值");
43                 return default(T);
44             }
45            
46         }
47 
48         public T Peek()
49         {
50             if (count>0)
51             {
52                 return top.Data;
53             }
54             else
55             {
56                 Console.WriteLine("栈是空的,无法取值");
57                 return default(T);
58             }
59         }
60     }
61 }


在控制台运行试试

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5           
 6             //链栈
 7             IStack<int> stack = new ListStack<int>();
 8             stack.Push(3);
 9             stack.Push(8);
10             Console.WriteLine(stack.Count);
11             Console.WriteLine(stack.Peek());
12             Console.WriteLine(stack.Count);
13             Console.WriteLine(stack.Pop());
14             Console.WriteLine(stack.Count);
15             Console.ReadLine();
16           
17         }
18     }

运行结果:

顺序栈和链栈都成功了。

 四、重点难点详解。

top初始值为-1或者是0,这都是什么和什么啊

top初始值为-1:

当你存储元素之后,top++,这时候top一直是指向栈顶元素的,这样做的好处是,栈的个数是top,而且stack[top]直接就是栈顶元素。缺点是调用Push时要Push(++top),栈的个数也是top+1.

top初始值为0:

top初始值为0的好处呢,就是Push的话直接Push(top),然后再top++;和栈的元素个数是top。稍微麻烦的是栈顶元素是stack[top-1].

其实top的初始值是-1还是0无关紧要,按照自己的习惯来就好,我是更倾向于top=-1;

顺序栈像一个只有一端开口的数组一样,链栈是怎么回事呀?

你可以把链栈理解为一个一个节点Node链接起来了,一个节点应该包括两部分,data和next.data里面装的是本节点的数据,next里面存储的是上一个节点的

 链栈是这样进行的

五:后续

看懂这篇博文之后大家只能对栈有一个大概的认识,不至于说起栈来不知所以。要想真正做到得心应手的去使用栈,还得靠大家自己去摸索。再会。

原文地址:https://www.cnblogs.com/yunquan/p/4889029.html