什么是栈?

    栈是限制插入和删除只能在同一个位置上进行的表,这个位置就是栈的顶端,对于栈的操作主要有三种形式:入栈(将元素插入到表中),出栈(将表最后的元素删除,也就是栈顶的元素),返回栈顶元素。栈有时又叫LIFO(后进先出)表。

栈的实现

栈可以有两种实现方式:1)数组实现 2)链表实现

栈的双向链表实现

    public class StackForLinked<T>
        {
                private int num = 0;
                public int count { get{return num;} }
                private Node<T> root = null;
                private class Node<T>
                {
                    public T data;
                    public Node<T> prev;
                    public Node<T> next;
                    public Node(T value)
                    {
                            this.data = value;
                            this.prev=null;
                            this.next=null;
                    }
                    public Node(T value,Node<T> prev,Node<T> next):this(value)
                    {
                            this.prev = prev;
                            this.next=next;
                    }
                }
                public void Push(T value)
                {
                    Node<T> node = new Node<T>(value);
                    if(root==null)
                    {
                            root = node;
                    }
                    else
                    {
                            var last= GetLastNode();
                            last.next = node;
                            node.prev= last;
                    }
                    num++;
                }
                public T Pop()
                {
                    var node=GetLastNode();
                    if(node==null)
                            throw new Exception();
                    node.prev.next=null;
                    num--;
                    return node.data;
                }
                public T GetTop()
                {
                    var node =GetLastNode();
                    if(node==null)
                            throw new Exception();
                    return node.data;
                }
                private Node<T> GetLastNode()
                {
                    Node<T> p = root;
                    if(root==null)
                    {
                         return null;
                    }
                    while(true)
                    {
                            if(p.next!=null)
                            {
                                p=p.next;
                            }
                            else
                                break;
                    }
                    return p;
                }
        }

栈的数组实现

    public class StackForArray<T>
        {
                private static int length = 100;
                private T[] objList = new T[length];
                private int currentIndex = 0;
                public int count { get { return this.currentIndex;} }
                private void ensureCapcity(int capcity)
                {
                    if(capcity < currentIndex)
                    {
                            return;
                    }
                    T[] old = objList;
                    objList = new T[capcity];
                    for(int i=0;i<old.Length;i++)
                    {
                            objList[i] = old[i];
                    }
                }
                public void Push(T value)
                {
                    if(currentIndex == length)
                    {
                            ensureCapcity(currentIndex * 2 +1);
                    }
                    objList[currentIndex] = value;
                    currentIndex++;
                }
                public T Pop()
                {
                    if(currentIndex == 0)
                            throw new Exception();
                    currentIndex--;
                    return objList[currentIndex];
                }
                public T GetTop()
                {
                    if(currentIndex == 0)
                    {
                            throw new Exception();
                    }
                    return objList[currentIndex -1];
                }
        }

PS:对于链表在添加和删除方面是O(1), 但是在查找方面则是O(N);数组在查找方面是O(1)操作,但是在插入或者是删除方面涉及到表的中元素的位置上的移动,若数据中的元素较多会带来性能上的损失。所以在考虑使用链表还是使用数组进行存储数据的时候,应该考虑上述所说(数据量大小,插入和删除多还是查找的多)。当前这与栈的关系不太大,因为栈都是在一端进行操作,不会涉及元素移动的问题。

栈的应用

栈的应用有很多,这里只举一些简单的例子:
1.平衡符号:例如判断括号是否成对出现,通过将括号入栈然后碰见成对的括号就出栈,最后判断栈中的元素是否为空,为空则是成对出现,反之则不成对
2.中缀表达式转化为后缀表达式:
    中缀表达式和后缀表达式: 例如表达式 a + b * c 这就是我们平时所见的正常的数学计算表达式,在数学上我们也知道怎么计算,但是你把这一段表达式告诉计算机,然后计算出结果,那么这个结果是怎么计算出来的呢?这里就涉及一个概念叫做后缀表达式,首先 a + b * c 会先通过栈转化为 a b c * + 然后再通过栈进行计算从而得出结果,前者通常叫做中缀表达式。随后会有一段代码的例子,可以让你真正的了解数学表达式是如何通过栈进行计算的
3.方法调用:类似于平衡符号的检测,所有的方法调用和返回都是成对的,在开始调用的时候需要在栈内存储一些信息(位置,对应变量的名字和返回地址等),方法完成后需要从栈中出栈拿到当初存储的信息然后做一些操作,这也正好印证了,当递归调用的时候,不当的递归会出现 Stackoverflow 异常,也就是说在栈(在window下,默认大小是1M )中已经存储不下每一次递归的信息了,就会报这个错误。

PS: 我这里说的都是同步方法,存在一个方法的调用需要等待另一个方法的完成,异步方法还不太了解,期待后续学习会有更多的收获

栈实现基本操作的运算

    用栈来实现简单的基本数学运算:+ ,- ,* , / 和( )在数学中我们都知道这几种符号的优先级,那么计算机时怎么做到的呢? 下面我们以一个表达式为例子来实现一下计算机的计算过程
    表达式:a + b * c + ( d * e + f ) * g ,除栈外,我们还需要一个输出字符串用来存储生成的后缀表达式,默认情况下为空,出于方便我会使用 [] 代表一个栈,[ 代表栈底,] 代表栈顶,用 | 分割每个元素。

原理

1.对常数输出,对操作符号入栈操作,根据优先级别判断是出栈还是入栈;
2.优先级低的不允许出现在优先级高的后面(栈中存在一个优先级高的符号,此时来了一个低优先级符号,需要进行先把高优先级的符号出栈,然后将低优先级的符号入栈);
3.同优先级的符号先出栈再入栈;
4.对于存在括号的情况,当前栈顶的元素是 ( 那么接下来的一个符号不比较优先级直接入栈,接下来的操作遵循前3条规则;
5.当遇见 )的时候,执行出栈操作直到遇见出栈的第一个( 为止;
6.得到后缀表达式之后再次使用栈进行计算,将后缀表达式中的值入栈,碰见操作符,出栈两个元素执行运算符计算,将计算的结果插入到栈中,直到后缀表达式的尽头,最终结果位于栈顶,出栈即可得到

步骤:

1.将中缀表达式转化为后缀表达式

Step 1 : 我们第一次读取的是常数 a ,直接输出,此时输出的字符串中的值为 a
Step 2 : 继续读取,接下来读取的是操作符号 + ,入栈操作,此时栈中的元素为 [ + ]
Step 3 : 继续读取,接下来读取的是常数 b , 直接输出,此时输出的字符串中的值为 a b
Step 4 : 继续读取,接下来读取的是操作符号 * , 入栈操作,此时栈中的元素为 [ + | * ]
Step 5 : 继续读取,接下来读取的是常数 c , 直接输出,此时输出的字符串中的值为 a b c
Step 6 : 继续读取,接下来读取的是操作符号 + , 栈顶元素 * 的优先级高于 + ,将 * 出栈,之后发现栈顶元素为 +,优先级相同,继续出栈,然后将 + 进栈,此时栈中的元素为[ + ],输出字符串中的值为 a b c * +
Step 7 : 继续读取,接下来读取的是操作符号 ( , 入栈操作,此时栈中的元素为 [ + | ( ]
Step 8 : 继续读取,接下来读取的是常数 d , 直接输出,此时输出的字符串中的值为 a b c * + d
Step 9 : 继续读取,接下来读取的是操作符号 * , 入栈操作,此时栈中的元素为[ + | ( | * ]
Step 10 : 继续读取,接下来读取的是常数 e , 直接输出,此时输出的字符串中的值为 a b c * + d e
Step 11 : 继续读取,接下来读取的是操作符号 + , 栈顶元素 * 的优先级高于 + ,先执行出栈操作,在执行 + 入栈操作,此时栈中的元素为[ + | ( | +] , 输出字符串中的值为 a b c * + d e *
Step 12 : 继续读取,接下来读取的是常数 f , 直接输出,此时输出的字符串中的值为 a b c * + d e * f
Step 13 : 继续读取,接下来读取的是操作符号 ) , 开始执行出栈操作,直到遇见第一个出栈的元素为 ( 停止,此时栈中的元素为[ + ] , 输出字符串中的值为 a b c * + d e * +
Step 14 : 继续读取,接下来读取的是操作符号 * , 入栈操作,此时栈中的元素为[ + | * ]
Step 15 : 继续读取,接下来读取的是常数 g , 直接输出,此时输出的字符串中的值为 a b c * + d e * f + g
Step 16: 继续读取,到达了末端,开始执行中出栈操作,直到栈为空,此时输出的字符串中的值为 a b c * + d e * f + g * + ,从而得出后缀表达式为:a b c * + d e * f + g * +

2.计算后缀表达式 (Step中的括号的使用是起到辅助清晰的作用,因为并没给字符实际的值)

Step 1 : 读取后缀表达式,第一个遇到的是 a 执行入栈操作,此时栈中的元素为 [ a ]
Step 2 : 继续读取,解下来遇到的是 b ,执行入栈操作,此时栈中的元素为 [ a | b ]
Step 3 : 继续读取,解下来遇到的是 c ,执行入栈操作,此时栈中的元素为 [ a | b | c ]
Step 4 : 继续读取,解下来遇到的是 * ,出栈两个元素,计算 b * c ,然后在执行入栈, 此时栈中的元素为 [ a | b * c ]
Step 5 : 继续读取,解下来遇到的是 + ,出栈两个元素,计算 a + b * c,然后执行入栈操作,此时栈中的元素为 [ a + b * c ]
Step 6 : 继续读取,解下来遇到的是 d ,执行入栈操作,此时栈中的元素为 [ a + b * c | d ]
Step 7 : 继续读取,解下来遇到的是 e ,执行入栈操作,此时栈中的元素为 [ a + b * c | d | e ]
Step 8 : 继续读取,解下来遇到的是 * ,出栈两个元素,计算 d * e ,然后执行入栈操作,此时栈中的元素为 [ a + b * c | d * e ]
Step 9 : 继续读取,解下来遇到的是 f ,执行入栈操作,此时栈中的元素为 [ a + b * c | d * e | f ]
Step 10 : 继续读取,解下来遇到的是 + ,出栈两个元素,计算 d * e + f ,然后执行入栈操作,此时栈中的元素为 [ a + b * c | d * e + f ]
Step 11 : 继续读取,解下来遇到的是 g ,执行入栈操作,此时栈中的元素为 [ a + b * c | d * e + f | g ]
Step 12 : 继续读取,解下来遇到的是 * ,出栈两个元素,计算 ( d * e + f ) * g ,然后执行入栈操作,此时栈中的元素为 [ a + b * c | ( d * e + f ) * g ]
Step 13 : 继续读取,解下来遇到的是 + ,出栈两个元素,计算 a + b * c + ( d * e + f ) * g ,然后执行入栈操作,此时栈中的元素为 [ a + b * c + ( d * e + f ) * g ]
Step 14 : 继续读取,到达了末端,执行出栈操作,出栈的结果就是计算的结果 a + b * c + ( d * e + f ) * g

实现代码

class Program
{
        static List<Priority> priority = new List<Priority>()
        {
        new Priority() { level = 0 , symbal ="+" },
        new Priority() { level = 0 , symbal ="-" },
        new Priority() { level = 1 , symbal ="*" },
        new Priority() { level = 1 , symbal ="/" },
        new Priority() { level = 2 , symbal ="%" },
        new Priority() { level = int.MaxValue , symbal ="(" },
        new Priority() { level = int.MaxValue , symbal =")" }
        };
        static void Main(string[] args)
        {
                //string expression = "a + b * c + ( d * e + f ) * g";
                //string expression ="10 + 1 * 10 + ( 2 * 3 + 4 ) * 10"; // 120
                //string expression ="1 + 2 + 3 ";
                string expression ="3 * ( ( 2 - 4 ) / ( 2 - 1 ) ) ";
                GenerateExpression(expression);
                Console.WriteLine(OutputStr);
                Console.WriteLine(CalculateValue());
        }
        ///生成后缀表达式的栈
        static StackForArray<Priority> symbalStack = new StackForArray<Priority>();
        /// 输出字符串
        static string OutputStr=string.Empty;
        /// 生成后缀表达式
        static void GenerateExpression(string expression)
        {
                string[] symbals = expression.Split(' ');
                foreach(string symbal in symbals)
                {
                        RecursionExpression(symbal);
                }
                while(true)
                {
                        try
                        {
                            var popElement = symbalStack.Pop();
                            OutputStr += popElement.symbal +" ";
                        }
                        catch(Exception ex)
                        {
                            break;
                        }
                }
        }
        // 计算表达式的值
        static int CalculateValue()
        {
                StackForArray<int> valueStack = new StackForArray<int>();
                string[] symbals = OutputStr.Split(new char[]{' '}
                        ,StringSplitOptions.RemoveEmptyEntries);
                foreach(var symbal in symbals)
                {
                        var temp = priority.Where(x=>x.symbal ==symbal).FirstOrDefault();
                        if(temp!=null)
                        {
                            var t = 0;
                            var second = valueStack.Pop();
                            var first = valueStack.Pop();
                            switch (symbal)
                            {
                                    case "*":
                                        t = first * second;
                                        break;
                                    case "/":
                                        t = first / second;
                                        break;
                                    case "+":
                                        t = first + second;
                                        break;
                                    case "-":
                                        t = first - second;
                                        break;
                                    default:
                                     throw new Exception();
                            }
                            valueStack.Push(t);
                        }
                        else
                        {
                            valueStack.Push(int.Parse(symbal));
                        }
                }
                return valueStack.Pop();
        }
        /// 递归表达式
        static void RecursionExpression(string symbal)
        {
                var temp = priority.Where(x=>x.symbal == symbal).FirstOrDefault();
            if(temp==null)
                {
                        OutputStr+= symbal + " ";
                        return;
                }
                var stackCount = symbalStack.count;
                if(stackCount==0 || temp.symbal =="(")
                {
                        symbalStack.Push(temp);
                        return;
                }
                if(temp.symbal ==")")
                {
                        while(true)
                        {
                            var popElement = symbalStack.Pop();
                            OutputStr += popElement.symbal +" ";
                            if(popElement.symbal=="(")
                            {
                                    OutputStr=OutputStr.Substring(0,OutputStr.Length -2);
                                    break;
                            }
                        }
                        return;
                }
                var topElement = symbalStack.GetTop();
                if(temp.level > topElement.level || topElement.symbal=="(")
                {
                        symbalStack.Push(temp);
                        return;
                }
                if(temp.level <= topElement.level && topElement.symbal != "(")
                {
                        var popElement= symbalStack.Pop();
                        OutputStr += popElement.symbal +" ";
                        RecursionExpression(temp.symbal);
                        return;
                }
            }
    }
    public class Priority
    {
        public string symbal { get; set; }

        public int level { get; set; }
    }
}

PS:测试的代码比较简单,主要看思路吧

数据结构基础:栈(Stack)的更多相关文章

  1. Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现

    栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...

  2. 算法与数据结构基础 - 堆栈(Stack)

    堆栈基础 堆栈(stack)具有“后进先出”的特性,利用这个特性我们可以用堆栈来解决这样一类问题:后续的输入会影响到前面的阶段性结果.线性地遍历输入并用stack处理,这类问题较简单,求解时间复杂度一 ...

  3. 数据结构之栈(Stack)

    什么是栈(Stack) 栈是一种遵循特定操作顺序的线性数据结构,遵循的顺序是先进后出(FILO:First In Last Out)或者后进先出(LIFO:Last In First Out). 比如 ...

  4. [ACM训练] 算法初级 之 数据结构 之 栈stack+队列queue (基础+进阶+POJ 1338+2442+1442)

    再次面对像栈和队列这样的相当基础的数据结构的学习,应该从多个方面,多维度去学习. 首先,这两个数据结构都是比较常用的,在标准库中都有对应的结构能够直接使用,所以第一个阶段应该是先学习直接来使用,下一个 ...

  5. 数据结构11: 栈(Stack)的概念和应用及C语言实现

    栈,线性表的一种特殊的存储结构.与学习过的线性表的不同之处在于栈只能从表的固定一端对数据进行插入和删除操作,另一端是封死的. 图1 栈结构示意图 由于栈只有一边开口存取数据,称开口的那一端为“栈顶”, ...

  6. Python与数据结构[1] -> 栈/Stack[1] -> 中缀表达式与后缀表达式的转换和计算

    中缀表达式与后缀表达式的转换和计算 目录 中缀表达式转换为后缀表达式 后缀表达式的计算 1 中缀表达式转换为后缀表达式 中缀表达式转换为后缀表达式的实现方式为: 依次获取中缀表达式的元素, 若元素为操 ...

  7. 线性数据结构之栈——Stack

    Linear data structures linear structures can be thought of as having two ends, whose items are order ...

  8. 算法与数据结构基础 - 二叉树(Binary Tree)

    二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...

  9. C# 数据结构 栈 Stack

    栈和队列是非常重要的两种数据结构,栈和队列也是线性结构,线性表.栈和队列这三种数据结构的数据元素和元素的逻辑关系也相同 差别在于:线性表的操作不受限制,栈和队列操作受限制(遵循一定的原则),因此栈和队 ...

  10. 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

随机推荐

  1. 浅谈卷积神经网络及matlab实现

    前言,好久不见,大家有没有想我啊.哈哈.今天我们来随便说说卷积神经网络. 1卷积神经网络的优点 卷积神经网络进行图像分类是深度学习关于图像处理的一个应用,卷积神经网络的优点是能够直接与图像像素进行卷积 ...

  2. 备份Rhythmbox播放器的曲目和播放列表信息

    Rhythmbox音乐播放器只能保存单个播放列表,如果在rhythmbox下建了很多播放列表(比如按歌手名分类),每个播放列表下包含一些歌曲,为了避免重装系统后重新建这些播放列表,可以备份下面的文件. ...

  3. printf和scanf整理(后续填补)

    scanf和printf头文件:<stdio.h> 1.%d.%3d.%03d.%-3d区分 %d:以十进制形式输出整数(int) %3d:指定宽度为3,不足的左边补空格 %03d:一种左 ...

  4. arcgis api for js入门开发系列十一地图统计图

    上一篇实现了demo的叠加SHP图层,本篇新增地图统计图,截图如下: 地图统计图实现的思路如下:利用拓展arcgis api的js文件(MapChartGraphic.js以及MapChartGrap ...

  5. 跨域访问之JSONP

    跨域 在平常的工作中常常会遇到A站点的需要访问B站点的资源. 这时就产生了跨域访问. 跨域是指从一个域名的网页去请求另一个域名的资源.浏览器遵循同源策略,不允许A站点的Javascript 读取B站点 ...

  6. Chapter 7. Design and Performance

    本章将对MPEG4及H.264的实现细节进行讲解和比对. Motion Estimation 衡量运动估计的好坏有三种函数(第228页):MSE,MAE和SAE,其中由于SAE运算速度最快所以采用的最 ...

  7. 不错的 HttpHelper类 c#

    /// <summary>/// 类说明:HttpHelper类,用来实现Http访问,Post或者Get方式的,直接访问,带Cookie的,带证书的等方式,可以设置代理/// 重要提示: ...

  8. 详解react/redux的服务端渲染:页面性能与SEO

        亟待解决的疑问 为什么服务端渲染首屏渲染快?(对比客户端首屏渲染)   react客户端渲染的一大痛点就是首屏渲染速度慢问题,因为react是一个单页面应用,大多数的资源需要在首次渲染前就加载 ...

  9. React Image加载图片过大导致ListView滑动卡顿

    今天莫名的发现ListView加载Item很卡,一顿一顿的... ListView Item 中只加载一张图片,小编从百度爸爸上随便复制的链接,这张图片很大,以致埋下如此大坑... Image的Sty ...

  10. 开源自己写的图片转Ascii码图工具

    GitHub地址:https://github.com/qiangzi7723/img2Ascii 如果觉得不错可以给个star或者提出你的建议 img2Ascii,基于JS的图片转ASCII示意图. ...