算法基础之栈的理论和应用

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情


1.栈的基础定义

官方定义:栈是限定仅在表尾进行插入和删除操作的线性表。
解释:栈也是一种线性结构,相比数组,栈对应的操作是数组的子集,只能从一端添加元素,也只能一端取出元素。
把允许删除和删除的一端称为栈顶(top),另一端称为栈底(bottom)
在这里插入图片描述
栈是一种**后进先出(Last in First out, LIFO)**的数据结构。
思考:最先进栈的元素,是不是就只能是最后出栈呢?当然不是啦!

2.栈的实现

因为栈的操作基本是数组操作的子集,我们只需要把栈初始化为一个空数组,再实现它的各个操作即可。
基本需求:
在这里插入图片描述

class Stack(object):
    """栈"""
    def __init__(self):
         self.items = []

    def is_empty(self):
        """判断是否为空"""
        return self.items == []

    def push(self, item):
        """加入元素"""
        self.items.append(item)

    def pop(self):
        """弹出元素"""
        return self.items.pop()

    def peek(self):
        """返回栈顶元素"""
        return self.items[len(self.items)-1]

    def size(self):
        """返回栈的大小"""
        return len(self.items)

用下面代码检验一下吧

if __name__ == "__main__":
    stack = Stack()
    stack.push("HELLO")
    stack.push("MY")
    stack.push("FRIEND")
    print(stack.items)
    print(stack.size())
    print(stack.peek())
    print(stack.pop())
    print(stack.items)
    print(stack.pop())
    print(stack.pop())
    print(stack.items)
['HELLO', 'MY', 'FRIEND']
3
FRIEND
FRIEND
['HELLO', 'MY']
MY
HELLO
[]

应该是OK了。

3.栈的应用

一般撤销操作应用到的就是栈的原理,接下来我们结合一些Leetcode题来领悟一下。

3.1有效的括号(20)

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

  1. Open brackets must be closed by the same type of brackets.
  2. Open brackets must be closed in the correct order.
    在这里插入图片描述
    这题让我们知道编译器是怎么知道括号是否匹配的呢,其实用的就是栈的原理。注意的是,翻译版题目里的‘闭合’其实就是匹配对的意思。
    先直观理解一下,不论大中小括号,只要是左括号就把它压入栈;如果是右括号,就看栈顶的是否是匹配的左括号,匹配则出栈。最终是空栈了,说明是合法的括号匹配。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    再来看个不合法的例子:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以,栈顶元素反映了在嵌套的层次关系中,最近的需要匹配的元素。
概括一下这题的思路:
从前向后扫描字符串:
遇到左括号X,就压栈x。
遇到右括号y:
如果发现栈顶元素x和该括号y匹配,则栈顶元素出栈,继续判断下一个字符;
如果栈顶元素x和该括号不匹配,字符串不匹配,如果栈为空,字符串不匹配。
扫描完成后,如果栈恰好为空,则字符串匹配,否则,字符串不匹配。

#有效的括号
class Solution:
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        
        stack = []
        for ch in s:
            if ch in ['(','[','{']:
                stack.append(ch)
            
            else:
                if stack:
                    left = stack.pop()#栈顶元素出栈
                else:
                    return False
                if left=='[' and ch ==']' or left=='(' and ch==')' or left=='{' and ch=='}':
                    continue
                else:
                    return False
        if stack == []:
            return True
        else:
            return False

3.2四则运算表达式求值

一般四则运算除了加减乘除,还有括号,我们一般称作中缀表达式比如下面例子:
9 + (3 - 1) * 3 + 10 / 2
然而机器更容易理解的是后缀表示法,即所有的符号要在运算数字的后面出现:
9 3 1 - 3 * + 10 2 / +
下面我们来看一下计算器怎么理解的,一开始从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号把栈顶的两个数字拿出来进行运算,运算结果再进栈。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因此,我们根据此原理写出它的实现:

#四则运算表达式
class Solution:
    def evalRPN(self, tokens):
        """
        :type tokens: List[str]
        :rtype: int
        """
        stack = []
 
        for s in tokens:
            if s in ['+','-','*','/']:
                b = stack.pop()
                a = stack.pop()
                if s == '+':
                    stack.append(a+b)
                elif s == '-':
                    stack.append(a-b)
                elif s == '*':
                    stack.append(a*b)
                else:
                    stack.append(int(a/b))
            else:
                stack.append(int(s))
                    
        ans = int(stack.pop())
        return ans

这时,我们会好奇那这个后缀表达式是怎么推导出来的呢?再用原来的例子解释一下,
中缀表达式:9 + (3 - 1) * 3 + 10 / 2
后缀表达式:9 3 1 - 3 * + 10 2 / +
中缀转换成后缀的规则是:从左到右遍历中缀表达式每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3设计一个有getMin功能的栈(代码面试题)

在这里插入图片描述
我们设计出俩个栈,一个是普通栈(stackData)里面放数据,另一个栈(stackMin)里面放栈中最小值。
第一种方法:在压栈时判断插入时为空栈或者元素小于等于当前栈顶元素,压入最小值栈中。如果弹栈元素大于等于(不可能小于)最小值栈顶元素,同时从最小值栈中弹出。最终,stackMin里从底到顶的元素大小是从大到小排的,因此最小值就是栈顶元素。
在这里插入图片描述

#设计一个有getMin功能的栈-方法1
class NewStack1:
    def __init__(self):
        self.stackData = []
        self.stackMin = []

    def push(self, newNum):
        self.stackData.append(newNum)
        if len(self.stackMin) == 0 or newNum <= self.getMin():
            self.stackMin.append(newNum)

    def pop(self):
        if len(self.stackData) == 0:
            raise Exception("stack is empty!")
        value = self.stackData.pop()
        if self.getMin() == value:
            self.stackMin.pop()
        return value

    def getMin(self):
        if len(self.stackMin) == 0:
            raise Exception("stack is empty!")
        return self.stackMin[-1]

第二种方法:插入的元素比当前栈中最小值大,将最小值重复入栈。弹栈时同时弹出数据栈和最小值栈中的一个元素。最终,stackMin里从底到顶的元素大小还是从大到小排的,因此最小值就是栈顶元素。
在这里插入图片描述

#设计一个有getMin功能的栈-方法2
class NewStack2:
    def __init__(self):
        self.stackData = []
        self.stackMin = []

    def push(self, newNum):
        self.stackData.append(newNum)
        if len(self.stackMin) == 0 or newNum < self.getMin():
            self.stackMin.append(newNum)
        else:
            self.stackMin.append(self.getMin())

    def pop(self):
        if len(self.stackData) == 0:
            raise Exception("Stack is empty!")
        self.stackMin.pop()
        return self.stackData.pop()

    def getMin(self):
        if len(self.stackMin) == 0:
            raise Exception("Stack is empty!")
        return self.stackMin[-1]
掌握算法核心:、队列与二叉树的实战应用 本章深入探讨了、队列以及二叉树在算法实现中的核心应用。通过具体的代码实现习题解析,展示了如何利用这些数据结构解决实际问题,如运算符优先级判断、后缀表达式转换以及二叉树遍历等。 阅读详情

相关推荐

深入理解队列:数据结构应用与实践

本章深入探讨了队列这两种基础数据结构应用,展示了它们如何在递归、函数调用、序列反转、语法检查、括号匹配、表达式求值等领域发挥作用。通过具体的编程练习,读者可以更好地理解掌握的后进先出(LIFO)队列的先进先出(FIFO)的工作原理。

weixin_35755640的博客 421

深入理解C语言中的理论与实践

本文从的基本概念出发,详细介绍了的性质、实现方法以及在实际项目中的应用。通过理论知识具体的C语言实现代码,学生党们可以全面掌握的使用技巧,为后续的编程学习打下坚实的基础。希望这篇博客对大家有所帮助。如果你有任何问题或建议,欢迎在评论区留言讨论!

2302_79999839的博客 1345

android 数据库锁的问题

最近在项目中遇到一个错误: 12-26 17:29:10.501 32592 32644 W SQLiteConnectionPool: The connection pool for database '***.db' has been unable to grant a connection to thread 1378 (thread-pool-0) with flags 0x2 for...

低调的成长 2889

数据结构算法的原理及C语言底层实现

是顺序表的一种,具有顺序表同样的存储结构,由数组定义,配合用数组下标表示的顶指针top成各种操作。是限制在一端进行插入操作删除操作的线性表。允许进行操作的一端称为顶,另一固定端称为底,当中没有元素时称为空具有后进先出的特点。

weixin_46216674的博客 1232

一文吃透结构:从基本操作到算法应用,零基础也能轻松掌握

本文介绍了的基本概念、实现及其应用是一种遵循"后进先出"原则的线性表,支持入、出等基本操作。文章详细解析了基于动态数组的实现代码,包括初始化、销毁、扩容策略等关键操作,并指出常见错误(如函数名拼写错误)。通过括号匹配的经典案例,展示了算法中的实际应用,强调在解决成对匹配问题时的高效性。测试代码演示了的操作流程,验证了实现的正确性。

2403_87581574的博客 1804

应用

一、 1、的介绍 定义:是只允许在一端插入或删除的特定线性表。 应用场景: 1、子程序的调用:在跳往子程序前,会将下个指令的地址存到堆中,直到子程序执行完后再将地址取出,以回到原来的程序中。 2、处理递归调用:子程序调用类似,除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆中。 3、 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)—使用操作数运算符来完成 4、二叉树的遍历—行遍历 5、图形的深度优先(depth—first)搜索法。 2、使用数组来模拟 思路分析:

AX_Chu的博客 2878

数据结构实验)应用-算术表达式求值

实验目的: 1.掌握的定义及实现; 2.掌握利用求解算术表达式的方法。   实验内容: 通过修改完善教材中的算法3.22,利用来实现算术表达式求值的算法。 对算法3.22中调用的几个函数要给出其实现过程: (1) 函数In(c):判断c是否为运算符; (2) 函数Precede(t1,t2):判断运算符t1t2的优先级; (3) 函数Operate(a,theta,b):...

烟敛寒林的博客 1万+

代码随想录算法训练营第九天|与队列理论基础、用实现队列、用队列实现、有效的括号、删除字符串中的所有相邻重复项

介绍了队列的基础理论及相互实现方法。在Java中,Stack类属于容器类型但不同于C++ STL的实现。重点讲解了如何用双模拟队列(stackIn负责入队,stackOut负责出队)用单队列实现(通过元素重排)。此外,还提供了有效的括号匹配算法(利用结构)删除字符串相邻重复项的解决方案(使用StringBuilder模拟)。这些方法都充分利用了数据结构的特性,通过巧妙设计实现功能转换。文章最后分享了作者的学习心得,展示了数据结构在实际编码中的灵活应用

weixin_75022175的博客 997

基础代码随想录算法学习9|与队列理论基础、232.用实现队列、225. 用队列实现、 20. 有效的括号、1047. 删除字符串中的所有相邻重复项

以保持的原始结构不变。(注意pop的区别)4、将que1赋值为que2,这样que1中就存储了原始中除了顶元素之外的其他元素。5、最后,使用while循环清空que2队列,以便下一次操作。

m0_51929270的博客 956

算法学习——与队列

如果还记得编译原理的话,编译器在词法分析的过程中处理括号、花括号等这个符号的逻辑,就是使用了这种数据结构。再举个例子,linux系统中,cd这个进入目录的命令我们应该再熟悉不过了。这个命令最后进入a目录,系统是如何知道进入了a目录呢 ,这就是应用。这在leetcode上也是一道题目,编号:71. 简化路径,大家有空可以做一下。

qq_24867003的博客 957

算法训练营第九天 | 与队列(一)

原文链接:https://programmercarl.com/1047.%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%E7%9B%B8%E9%82%BB%E9%87%8D%E5%A4%8D%E9%A1%B9.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE。,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

Nayut_的博客 608

代码随想录:队列

stackqueue不是容器container而是容器适配器container adapter不提供迭代器不能随机访问的底层实现可以是vector,deque,list注意queuedeque的差别deque是双开口容器,类比vector,内存是连续空间的block用指针连接起来的分段内存,vector是连续内存,deque查找效率比vector高。

blasttrapnest的博客 451

《代码随想录》刷题打卡day9:与队列part01

SGI STL。

2301_77038122的博客 700

代码随想录刷题——6与队列

代码随想录刷题——与队列

m0_46254725的博客 826

第三章队列的应用知识体系III

一、应用案例分析 1.数制的转换 【案例分析】 当将一个十进制整数N转换为八进制数时,在计算过程中,把N与8求余得到的八进制数的各位依次进,计算完毕后将中的八进制数依次出输出,输出结果就是待求得的八进制数。 【案例实现】 可以采用顺序存储表示也可以采用链式存储表示。 【算法步骤】 ① 初始化一个空S。 ② 当十进制数N非零时,循环执行以下操作:     ·把N与8求余得到的八进制数压入S;     ·N更新为N与

weixin_42705436的博客 375

数据结构算法基础-java版

数据结构算法基础-java版

Major_S的博客 838

力扣100题解及笔记

单调中保留的都是比当前入元素大的值,从顶到底 的元素是单调递增快速找到一个元素旁边第一个比它大或者比它小的元素用一个来记录遍历过的元素下标i,本质是空间换时间。

hiahiakk的博客 601

代码随想录代码训练营第十天 | 232.用实现队列、225. 用队列实现、20. 有效的括号、1047. 删除字符串中的所有相邻重复项

代码随想录代码训练营第十天 | 232.用实现队列、225. 用队列实现、20. 有效的括号、1047. 删除字符串中的所有相邻重复项

2301_81109252的博客 842

代码随想录Day5 队列

对于的经典题目 差不多就是括号匹配 或者什么算后缀表达式 总之就是如果相匹配就弹出 如果匹配失败 就压入对于队列来说单调队列 优先队列是要掌握的t=N7T8优先队列详解https://blog.csdn.net/weixin_52115456/article/details/127606811?

m0_73740071的博客 2073

数据结构算法文章列表

前言 数据结构算法文章列表主要内容包括:稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)等(正在更新)。 数据结构算法文章列表 数据结构算法:稀疏sparsearray数组

分享计算机基础,android,C/C++,音视频等内容 2572
上一篇: 算法基础之数组双指针(Leetcode题型归纳)
下一篇: 算法基础之队列的理论和应用
孤岛使者
博客等级 码龄6年 40粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值