堆栈的基础知识与相关题目(Python)

1.堆栈基础知识

堆栈:简称为栈。一种线性表数据结构,是一种只允许在表的一端进行插入和删除操作的线性表。

把栈中允许插入和删除的一端称为 「栈顶(top)」;另一端则称为 「栈底(bottom)」。当表中没有任何数据元素时,称之为 「空栈」。

两种基本操作:「插入操作」「删除操作」

  • 栈的插入操作又称为「入栈」或者「进栈」。
  • 栈的删除操作又称为「出栈」或者「退栈」。

简单来说,栈是一种 「后进先出(Last In First Out)」 的线性表,简称为 「LIFO 结构」

线性表:栈首先是一个线性表,栈中元素具有前驱后继的线性关系。栈中元素按照a1、a2…an 的次序依次进栈。栈顶元素为 。栈顶元素为an。
后进先出原则:根据堆栈的定义,每次删除的总是堆栈中当前的栈顶元素,即最后进入堆栈的元素。而在进栈时,最先进入堆栈的元素一定在栈底,最后进入堆栈的元素一定在栈顶。所以,元素进入堆栈或者退出退栈是按照【后进先出】的原则进行的。

1.1 堆栈的顺序存储与链式存储

栈有两种存储表示方法:顺序栈链式栈
顺序栈:即堆栈的顺序存储结构。利用一组地址连续的存储单元依次存放自栈底到栈顶的元素,同时使用指针top指示栈顶元素在顺序栈中的位置。
链式栈: 即堆栈的链式存储结构。利用单链表的方式来实现堆栈。栈中元素按照插入顺序依次插入到链表的第一个节点之前,并使用栈顶指针 top 指示栈顶元素,top 永远指向链表的头节点位置。

1.2 堆栈的基本操作

栈作为一种线性表来说,理论上应该具备线性表所有的操作特性,但由于「后进先出」的特殊性,所以针对栈的操作进行了一些变化。尤其是插入操作和删除操作,改为了入栈(push)和出栈(pop)。

1.2.1 堆栈的顺序存储基本操作

在这里插入图片描述
基本操作描述(大概看下整体情况,不清楚地方结合实例)
假设 self.top 指向栈顶元素所在位置
初始化空栈:使用列表创建一个空栈,定义栈的大小 self.size,并令栈顶元素指针 self.top 指向 -1,即 self.top = -1。
判断栈是否为空:当 self.top == -1 时,说明堆栈为空,返回 True,否则返回 False。
获取栈顶元素:先判断队列是否为空,为空直接抛出异常。不为空则返回 self.top 指向的栈顶元素,即 self.stack[self.top]。
插入元素(进栈、入栈):先判断队列是否已满,已满直接抛出异常。如果队列未满,则在 self.stack 末尾插入新的数据元素,并令 self.top 向右移动 1 位。
删除元素(出栈、退栈):先判断队列是否为空,为空直接抛出异常。如果队列不为空,则令 self.top 向左移动 1 位,并返回 self.stack[self.top]。
代码

class Stack:
    # 初始化空栈,大小为100
    def __init__(self, size=100):
        self.stack = []
        self.size = size
        self.top = -1    
        
    # 判断栈是否为空
    def is_empty(self):
        return self.top == -1
    
    # 判断栈是否已满
    def is_full(self):
        return self.top + 1 == self.size
    
    # 入栈操作
    def push(self, value):
        if self.is_full():
            raise Exception('Stack is full')
        else:
            self.stack.append(value)
            self.top += 1
    
    # 出栈操作
    def pop(self):
        if self.is_empty():
            raise Exception('Stack is empty')
        else:
            self.top -= 1
            self.stack.pop()
    
    # 获取栈顶元素
    def peek(self):
        if self.is_empty():
            raise Exception('Stack is empty')
        else:
            return self.stack[self.top]

1.2.2 堆栈的链式存储基本操作

堆栈的顺序存储结构存在的问题:在栈满或者其他需要重新调整存储空间时需要移动大量元素。堆栈可以采用链式存储方式可以解决上述问题,在 Python 中我们通过构造链表节点 Node 的方式来实现。这种采用链式存储结构的堆栈也被称为 「链式栈」
在这里插入图片描述
基本操作描述(大概看下整体情况,不清楚地方结合实例)
假设self.top指向栈顶元素所在位置
初始化空栈:使用列表创建一个空栈,并令栈顶元素指针 self.top 指向 None,即 self.top = None。

判断栈是否为空:当 self.top == None 时,说明堆栈为空,返回 True,否则返回 False。

获取栈顶元素:先判断队列是否为空,为空直接抛出异常。不为空则返回 self.top 指向的栈顶节点,即 self.top.value。

插入元素(进栈、入栈):创建值为 value 的链表节点,插入到链表头节点之前,并令栈顶指针 self.top 指向新的头节点。

删除元素(出栈、退栈):先判断队列是否为空,为空直接抛出异常。如果队列不为空,则令 self.top 链表移动 1 位,并返回 self.top.value。
代码

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None
        
class Stack:
    # 初始化空栈
    def __init__(self):
        self.top = None
    
    # 判断栈是否为空
    def is_empty(self):
        return self.top == None
    
    # 入栈操作
    def push(self, value):
        cur = Node(value)
        cur.next = self.top
        self.top = cur
    
    # 出栈操作
    def pop(self):
        if self.is_empty():
            raise Exception('Stack is empty')
        else:
            cur = self.top
            self.top = self.top.next
            del cur
    
    # 获取栈顶元素
    def peek(self):
        if self.is_empty():
            raise Exception('Stack is empty')
        else:
            return self.top.value

1.3 堆栈的案例

堆栈的优势:

  • 使用堆栈可以很方便的保存和取用信息,因此长被用作算法和程序中的辅助存储结构,临时保存信息,供后面操作中使用。
    例如:操作系统中的函数调用栈,浏览器中的前进、后退功能。
  • 堆栈的后进先出规则,可以保证特定的存取顺序
    例如:翻转一组元素的顺序、铁路列车车辆调度。

1.3.1 题一:最小栈

155. 最小栈

部分过程
在这里插入图片描述
代码

class MinStack:
    def __init__(self):
        # stack元素栈 用于存储元素
        self.stack = []
        # min_stack辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值
        self.min_stack = [math.inf]

    def push(self, x: int) -> None:
        self.stack.append(x)
        self.min_stack.append(min(x, self.min_stack[-1]))

    def pop(self) -> None:
        self.stack.pop()
        self.min_stack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.min_stack[-1]

1.3.2 题二:有效的括号

20. 有效的括号

思路
假如输入是 [{]},每种括号的左右数量分别相等,但不是有效的括号。这是因为结果还与括号的位置有关。
仔细分析我们发现,对于有效的括号,它的部分子表达式仍然是有效的括号,比如 {()[()]} 是一个有效的括号,()[{}] 是有效的括号,[()] 也是有效的括号。并且当我们每次删除一个最小的括号对时,我们会逐渐将括号删除完。比如下面的例子。
在这里插入图片描述
这个思考的过程其实就是栈的实现过程。因此我们考虑使用栈,当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反之不是。

代码

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 == 1:
            return False
        
        pairs = {
   
            ")": "(",
            "]": "[",
            "}": "{",
        }
        stack = list()
        for ch in s:
            if ch 
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值