Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >栈(Stack) 原

栈(Stack) 原

作者头像
云飞扬
发布于 2019-03-13 02:19:15
发布于 2019-03-13 02:19:15
77900
代码可运行
举报
文章被收录于专栏:星汉技术星汉技术
运行总次数:0
代码可运行

1.概念

栈又称堆栈,是限制在表的一端进行插入和删除运算的线性表。 表中进行插入、删除操作的一端称为栈顶(top)。 栈顶保存的元素称为栈顶元素。 表的另一端称为栈底(bottom)。 当栈中没有元素时称为空栈。 向一个栈中插入元素称为进栈入栈压栈(push)。插入的元素是当前最新的。 从一个栈中删除元素称为出栈退栈弹栈(pop)。删除的元素是当前最新的。 由于栈的插入和删除仅在栈顶进行,后进栈的元素必定先出栈,所以把堆栈称为后进先出表(Last In First Out,LIFO)。 当栈满时进栈运算称为上溢;当栈空时出栈运算称为下溢

2.ADT定义

基本操作和ADT定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ADT Stack{
  数据对象:D={ai|ai∈element,i=1,2,3……,n,n≥0}
  数据关系:R={<ai-1,ai>|ai-1,ai∈D,约定an端为栈顶,a1端为栈底}
  基本操作:
  init(int s);			//构造一个空栈s
  clear();				//清空栈
  isEmpty();			//判断堆栈是否为空
  isFull();				//判断栈是否满。
  peek();				//获取栈顶元素但不出栈
  push(Object o);		//元素o入栈
  pop();				//栈顶元素出栈
  getSize();			//获取元素个数。

}ADT Stack

3.分类

堆栈的存储结构有顺序存储结构和链式存储结构两种。 在顺序存储结构中要考虑堆栈的上溢;在链式存储结构中要考虑堆栈的下溢。 堆栈上溢是一种出错状态,应该设法避免它;堆栈下溢可能是正常现象,通常下溢用来作为程序控制转移的条件。 就线性表而言,实现栈的方法有很多,这里着重介绍顺序栈(arrary based stack)和链式栈(linked stack)。

1>顺序栈

顺序栈(arrary based stack)的实现从本质上讲,就是顺序线性表实现的简化。 如果用数组来实现,唯一要确定的是使用哪一端来表示栈顶。 如果使用数组的其实位置来作为栈顶,那么在删除和插入的时候会有很大的时间消耗,因为平移元素。 如果使用素组的尾端来作为栈顶,那么就不需要移动元素了。

①基本运算

堆栈的运算主要考虑入栈和出栈的算法。 入栈时需要考虑的操作步骤是堆栈初始化,然后判断堆栈是否为满,如果不满,则可以插入元素。 出栈时,需要考虑的步骤是判断堆栈是否为空,如果不空,删除元素,出栈之前,保存栈顶元素。

②顺序栈共享空间

堆栈顺序存储时,为避免上溢,需要首先分配较大空间,但这容易造成大量的空间浪费。所以当使用两个栈时,可以将两个栈的栈底设在向量空间的两端,让两个栈各自向中间靠拢,使空间得以共享。逻辑图如下:

具体实现方法 利用一个数组来存储两个堆栈,每个栈各自的断点向中间延伸。

③实现

利用数组实现一个顺序栈。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 使用数组实现堆栈
 * @author 星汉
 *
 */
public class ListStack {

    private Object[] elements;//数组容器
    private int size;//元素个数
    /**
     * 默认大小为32的堆栈
     */
    public ListStack() {
        this.elements=new Object[32];
        this.size=0;
    }
    /**
     * 清空栈
     * @return 成功返回true
     */
    public boolean clear() {
        for(int i=size-1;i>=0;i--,size--) {
            elements[i]=null;
        }
        return size<=0;
    }
    /**
     * 判断栈是否为空
     * @return 为空返回true
     */
    public boolean isEmpty() {
        return size==0;
    }
    /**
     * 判断是否满栈
     * @return 栈满返回true
     */
    public boolean isFull() {
        return size==elements.length;
    }
    /**
     * 获取栈顶元素,但不删除栈顶元素
     * @return object 栈顶元素
     */
    public Object peek() {
        if(size!=0) {
            return elements[size-1];
        }
        return null;
    }
    /**
     * 入栈
     * @param o 入栈元素
     */
    public void push(Object o) {
        if(size<elements.length) {
            elements[size]=o;
            size++;
        }else {
            System.out.println("上溢");
        }
    }
    /**
     * 出栈
     * @return 栈顶元素
     */
    public Object pop() {
        if(size>0) {
            Object tmp = elements[size-1];
            elements[size-1]=null;
            size--;
            return tmp;
        }else {
            System.out.println("下溢");
            return null;
        }
    }
    /**
     * 返回栈中元素个数
     * @return
     */
    public int getSize() {
        return size;
    }
}

2>链式栈

堆栈的链式存储称为链栈,即采用链表作为存储结构实现的栈。链式栈的基本运算同顺序栈。它是对链表实现的简单化。 使用单向链表实现的栈只能对表头进行操作,因为不能反向查找。

3>顺序栈和链式栈对比

实现顺序栈和链式栈都需要常数时间。 顺序堆栈初始时,需要说明一个固定的长度,当堆栈不够满时,会造成空间浪费。 链式栈的长度可变,不需要预先设定,相对比较节省空间,但是每个结点中设置一个指针域会产生结构开销。 顺序栈可以实现共享空间。 链式栈一般不用。

4.应用

堆栈的应用例子比较多,但比较典型的是数制的转换、表达式的计算、转换问题和递归问题。

1>数制转换

数制转换的基本原理: N mod d的值是余数,余数作为转化后的值,用N div d的商再作为N的值,再求余数,依次类推,直到商数为零。最后所得的余数反向输出,就是需要的结果。 由上述原理可以看出,这个过程恰好满足栈的运算规则:先进后出。所以可以使用堆栈实现数制的转换。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 数制转换
 * @author 星汉
 *
 */
public class TransportNum {

    public static void main(String[] args) {
        ListStack ls=new ListStack();//利用上面实现的栈
        int n=20;//未知数
        int remainder=0;//余数
        int d=2;//进制数
        while(n!=0) {
            remainder=n%d;
            n=n/d;
            ls.push(remainder);//将余数入栈
        }
        while(!ls.isEmpty()){
            System.out.print(ls.pop());
        }
    }
}

2>表达式的转换

表达式一般有中缀表达式、后缀表达式和前缀表达式3种表现形式。现实生活中使用的是中缀表达式,计算机内存储表达式时一般采用后缀或前缀表达式。 一个表达式通常由操作数、运算符及分隔符所构成。 中缀表达式就是将运算符放在操作数中间,例如:a+b*c 由于运算符有优先级,所以在计算机内部使用中缀表达式是非常不方便的。为了方便处理起见,一般需要将中缀表达式利用堆栈转换成为计算机比较容易识别的前缀表达式或者后缀表达式。 例如:前缀表达式:+a*bc        后缀表达式:abc*+ 其转换过程按照优先级转换,运算符的优先级顺序表如下图:

以中缀表达式a/(b-c)为例,演示一下中缀表达式转换为前缀表达式的具体步骤: 第一步:先处理优先级高的,括号内将(b-c)转换为(-bc)。 第二步:将除号进行处理为/a,整个表达式为/a(-bc)。 第三步:消除括号为/a-bc,就是将中缀表达式转为前缀表达式。

利用堆栈处理中缀表达式的步骤如下: 第一步:设置两个堆栈,一个操作数堆栈和一个运算符堆栈。 第二步:初始时为空,读取表达式时,只要督导操作数,将操作数压入操作数栈。 第三步:当读到运算符时将新运算符和栈顶运算符的优先级比较,如果新运算符的优先级高于栈顶运算符的优先级,将新运算符压入运算符堆栈;否则取出栈顶的运算符,同时取出操作数堆栈中的两个操作数进行计算,计算结果压入操作数堆栈。

中缀表达式的计算需要使用两个堆栈,并且计算比较频繁,而后缀或前缀表达式的实现只需要一个堆栈。 将中缀表达式转换为后缀表达式,转换原则如下: 第一:从左至右读取一个中缀表达式。 第二:若读取的是操作数,则直接输出。 第三:若读取的是运算符,分三种情况: 1.该运算符为左括号“(”,则直接存入堆栈。 2.该运算符为右括号“)”,则输出堆栈的运算符,直接取出左括号为止。 3.该运算符为费括号运算符,则与堆栈顶端的运算符做优先权比较,若堆栈顶端运算符高或者相等,则直接存入栈;若较栈顶端的运算符低,则输出堆栈中的运算符。 第四:当表达式已经读取完成,而堆栈中尚有运算符时,则依次序取出运算符,知道堆栈为空,由此得到的结果就是中缀表达式转换成的后缀表达式。

3>递归

递归问题实际上是程序或函数重复调用自己,并传入不同的变量来执行一种程序。 递归程序编写虽然简单,但在时间和空间上往往是不节省的。 递归是一种比较好的程序设计方法,比较典型的范例是汉内塔、数学上的阶乘以及最大公因子等问题。下面仅以阶乘问题来说明递归。 阶乘定义为:if n!=1 n=0   if n!=n*(n-1)! n>1 程序设计方法: 第一递归结束条件,当阶乘小于或等于1时,返回1。 第二递归执行部分,当阶乘大于1时,返回n!=n*(n-1)!。 实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static int recursion(int n) {
        if(n<=1) {
            return 1;
        }else {
            return n*recursion(n-1);
        }
}

4>递归的非递归实现

在递归程序中,主要就是一个堆栈的变化过程,程序执行过程中,堆栈是由系统自动实现的,但是我们应该能够将递归的程序变为非递归的实现。 非递归程序中,需要了解的是什么数据需要或什么时间压入堆栈,什么数据需要或在什么时候出堆栈。 例如上例中的阶乘问题,使用非递归实现,可以考虑实现将不同的n压入堆栈,每次减1,最后能够实现0的阶乘的计算,然后返回,知道堆栈为空为止。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static void main(String[] args) {
        //使用之前实现的链式堆栈
        LinkedStack ls=new LinkedStack();
        int num=10;
        while(num!=0) {
            ls.push(num);
            num--;
        }
        int product=1;
        while(!ls.isEmpty()) {
            product*=(int)ls.pop();
        }
        System.out.println(product);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/09/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
关于栈的三种表达式
前缀表达式也称为波兰表达式,前缀表达式的运算符位于操作数之前 如 ( 3 + 4 ) * 5 - 6 对应的前缀表达式为 - * + 3 4 5 6
切图仔
2022/09/14
3290
前缀、中缀、后缀表达式
转至: 前缀、中缀、后缀表达式 它们都是对表达式的记法,因此也被称为前缀记法、中缀记法和后缀记法。它们之间的区别在于运算符相对与操作数的位置不同:前缀表达式的运算符位于与其相关的操作数之前;中缀和后缀
Christal_R
2017/12/25
1.1K0
四则运算表达式求值
表达式求值 对于表达式的求值,一般使用中缀表达式转后缀表达式后,对后缀表达式求值,因为对于后缀或者前缀表达式计算,计算的顺序都是唯一的. 中缀表达式转后缀表达式的方法: 1.遇到操作数:直接输出(添加到后缀表达式中) 2.栈为空时,遇到运算符,直接入栈 3.遇到左括号:将其入栈 4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。 5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈 6.最终将栈中的元素依次出栈,输出。123
DuncanZhou
2018/09/04
5760
前缀、中缀、后缀表达式「建议收藏」
关键字:概念, 前缀表达式, 前缀记法, 中缀表达式, 中缀记法, 波兰式, 后缀表达式, 后缀记法, 逆波兰式
全栈程序员站长
2022/07/05
2.4K0
栈(2)
从右往左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式结果。
JusterZhu
2022/12/07
2470
栈(2)
计算机是如何理解表达式的?从中缀到后缀一文搞定!
在学习数据结构和算法时,我们常常会接触到表达式的不同写法,它们分别是中缀表达式、前缀表达式和后缀表达式。我们日常最熟悉的表达形式就是中缀表达式,如下所示:
Eulogy
2025/04/21
1790
计算机是如何理解表达式的?从中缀到后缀一文搞定!
栈(stack)的应用
版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/82728726
zy010101
2019/05/25
1.3K0
五分钟小知识之什么是后缀表达式
后缀表达式,又称逆波兰式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。
五分钟学算法
2019/05/23
3.7K0
五分钟小知识之什么是后缀表达式
C++ 使用栈求解中缀、后缀表达式的值
表达式求值对于有知识经验的人类而言,可以通过认知,按运算符的优先级进行先后运算。但对计算机而言,表达式仅是一串普通的信息而已,需要通过编码的方式告诉计算机运算法则。这个过程则需要借助于栈来实现。
一枚大果壳
2022/12/20
9440
C++ 使用栈求解中缀、后缀表达式的值
波兰表达式 与 逆波兰表达式
逆波兰表达式(Reverse Polish Notation,RPN),又称为后缀表达式,是一种特殊的算术表达式形式。
2025/01/20
2640
波兰表达式 与 逆波兰表达式
Java数据结构和算法(六)——前缀、中缀、后缀表达式
  前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功
IT可乐
2018/01/04
1.8K0
Java数据结构和算法(六)——前缀、中缀、后缀表达式
【数据结构】C语言实现表达式的转换
大家好!很高兴又和大家见面啦!!! 经过前面的学习,我们已经了解了表达式的三种形式以及它们对应的组成结构:
蒙奇D索隆
2024/09/07
1690
【数据结构】C语言实现表达式的转换
六十四、前缀,后缀,中缀表达式转化求值问题
上次介绍如何利用栈实现中缀表达式求值,如果我是出题官,当然要考前缀,后缀,中缀表达式相互转换,然后就变成了利用栈实现前缀和后缀表达式求值。
润森
2022/08/17
4160
数据结构与算法-(7)---栈的应用拓展-前缀表达式转换+求值
之前我们介绍过了什么是后缀表达式,以及它如何通过中缀表达式进行转换,以及关于后缀表达式的求值问题,如有遗忘👉🔗http://t.csdnimg.cn/Hl4Y9
ImAileen
2024/01/18
2650
数据结构与算法-(7)---栈的应用拓展-前缀表达式转换+求值
golang 计算器实现
  可能有读者会疑惑我们为什么将num定义为int,我们这么做的原因是为了简便,或者说就是偷懒吧,因为如果要支持使用者输入小数,那么我们的程序在获取、处理输入方面的代码会更加复杂一点╮(╯_╰)╭。关于如何获取、处理输入,我们将在本文的最后给出答案。同时也会给出完整的计算器程序代码,或者说是给出完整的只支持整数输入的、不具备查错纠错能力的四则运算计算器
golangLeetcode
2022/08/02
8950
golang 计算器实现
前缀、中缀、后缀表达式
它们都是对表达式的记法,因此也被称为前缀记法、中缀记法和后缀记法。它们之间的区别在于运算符相对与操作数的位置不同:前缀表达式的运算符位于与其相关的操作数之前;中缀和后缀同理。 举例: (3 + 4) × 5 - 6 就是中缀表达式 - × + 3 4 5 6 前缀表达式 3 4 + 5 × 6 - 后缀表达式 中缀表达式(中缀记法) 中缀表达式是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。中缀表达式是人们常用的算术表示方法。 虽然人的大脑很容易理解与分析中缀表达式,但对计算
_gongluck
2018/03/08
1.3K0
# 栈 栈的一个实际需求 栈的介绍 栈的应用场景 代码实现 栈实现综合计算器 # 栈的一个实际需求 请输入一个表达式 计算式:[722-5+1-5+3-3]点击计算【如下图】 请问:计算机底层是如何
用户9615083
2022/12/30
4500
栈
[数据结构与算法] 邂逅栈
栈的英文为(stack): 又名堆栈,它是一种运算受限的线性表。限定仅在表尾(栈顶)进行插入和删除操作的线性表
时间静止不是简史
2020/07/25
4940
Java数据结构与算法解析(二)——栈
栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶。对栈的基本操作有push(进栈)和pop(出栈),对空栈进行push和pop,一般被认为栈ADT的一个错误。当push时空间用尽是一个实现限制,而不是ADT错误。栈有时又叫做LIFO(后进先出)表。
老马的编程之旅
2022/06/22
3420
Java数据结构与算法解析(二)——栈
面试题解法二:逆波兰表达式计算'1 + (5 - 2) * 3'
昨天发了一个面试题:关于一道面试题【字符串 ‘1 + (5 - 2) * 3’,怎么算出结果为10,’eval’除外】,受到了各位大大的指点,用一个比较简单的解法就能够计算出来,因此自己在下班后按照各位的指点又实现了一遍,这里贴出来供大家参考。 了解前缀、中缀、后缀表达式 关于概念这里简单贴一下,想了解更多的可以自行Google 前缀表达式:是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”
糊糊糊糊糊了
2018/05/09
2K0
面试题解法二:逆波兰表达式计算'1 + (5 - 2) * 3'
相关推荐
关于栈的三种表达式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档