zoukankan      html  css  js  c++  java
  • 数据结构与算法之栈

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

    一、什么是栈?

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

      

      看到没,你如果把栈当成一个开口向上的容器的话,你最先存储的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里面存储的是上一个节点的

     链栈是这样进行的

    五:后续

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

  • 相关阅读:
    BI 和报表有什么区别
    软件评测师考试总复习(可靠性测试)
    软件评测师考试总复习(易用性测试)
    软件评测师考试总复习(标准符合性测试)
    软件评测师考试总复习(兼容性测试)
    软件评测师考试总复习(安全性测试与评估)
    软件评测师考试总复习(网络测试)
    软件评测师考试总复习(软件测试)
    软件评测师考试总复习(数据库技术)
    软件评测师考试总复习(WEB应用测试)
  • 原文地址:https://www.cnblogs.com/yunquan/p/4889029.html
Copyright © 2011-2022 走看看