武汉大学导航工程专业本科生,暑假自学Git-hub上的开源项目《hello算法》,该开源项目的地址为https://www.hello-algo.com/
今天要讲的是数据结构中的栈。
一、什么是栈?
栈(stack)是一种遵循先入后出逻辑的线性数据结构。我们可以把栈看出是一个羽毛球桶,往里装羽毛球的过程是入栈,取羽毛球的过程是出栈,先进的羽毛球后出,后进的先出。
我们把堆叠元素的顶部称为“栈顶”,底部称为“栈底”。将把元素添加到栈顶的操作叫作“入 栈”,删除栈顶元素的操作叫作“出栈”。
二、栈的常用操作:
方法 描述 时间复杂度
push() 元素入栈(添加至栈顶) 𝑂(1)
pop() 栈顶元素出栈 𝑂(1)
peek() 访问栈顶元素 𝑂(1)
python里面的list类可以直接使用上面的方法
stack: list[int] = []
# 元素入栈
stack.append(1)
stack.append(3)
stack.append(2)
stack.append(5)
stack.append(4)
# 访问栈顶元素
peek: int = stack[-1]
# 元素出栈
pop: int = stack.pop()
# 获取栈的长度
size: int = len(stack)
# 判断是否为空
is_empty: bool = len(stack) == 0
三、栈的实现
1.基于链表的实现
使用链表实现栈时,我们可以将链表的头节点视为栈顶,尾节点视为栈底
class ListNode:
def __init__(self,val:int):
self.val:int=val#节点值
self.next:ListNode|None=None#指向下一节点的引用
class LinkedListStack:
def __init__(self):
self._peek:ListNode|None=None
self._size:int=0
def size(self)->int:
return self._size
def is_empty(self)->bool:
return self._size==0
def push(self,val:int):#添加新元素的函数
node=ListNode(val)#入栈的节点
node.next=self._peek#新节点指向原来的栈顶元素
self._peek=node#新的栈顶元素
self._size+=1#栈的大小加1
def pop(self)->int:#栈顶元素出栈的函数
num=self.peek()
self._peek=self._peek.next
self._size-=1
return num
def peek(self)->int:#访问栈顶元素的函数
if self.is_empty():
raise IndexError("栈为空")
return self._peek.val
def to_list(self)->list[int]:
arr=[]
node=self._peek
while node:
arr.append(node.val)
node=node.next
arr.reverse()#反转列表
return arr
2.基于数组的实现
class ArrayStack:
def __init__(self):
self._stack:list[int]=[]
def size(self)->int:
return len(self._stack)
def is_empty(self)->bool:
return self.size()==0
def push(self,val:int):
self._stack.append(val)#入栈
def pop(self)->int:
if self.is_empty():
raise IndexError("栈为空")
return self._stack.pop()#出栈
def peek(self)->int:
if self.is_empty():
raise IndexError("栈为空")
return self._stack[-1]#访问栈顶元素
def to_list(self)->list[int]:
return self._stack#返回列表用于打印
A=ArrayStack()#定义一个类对象
A.push(1)
A.push(2)
arr=A.to_list()
print(arr)
#两种实现对比
#时间效率:
#‧ 基于数组实现的栈在触发扩容时效率会降低,但由于扩容是低频操作,因此平均效率更高。
#‧ 基于链表实现的栈可以提供更加稳定的效率表现。
#空间效率:
#基于数组实现的栈可能造成一定的空间浪费。然而,由于链表节点需要额外存储指针,因此链表节点占用的空间相对较大。
四、每日一题
题目来源于leetcode,网址如下:1021. 删除最外层的括号 - 力扣(LeetCode)
有效括号字符串为空 ""
、"(" + A + ")"
或 A + B
,其中 A
和 B
都是有效的括号字符串,+
代表字符串的连接。
- 例如,
""
,"()"
,"(())()"
和"(()(()))"
都是有效的括号字符串。
如果有效字符串 s
非空,且不存在将其拆分为 s = A + B
的方法,我们称其为原语(primitive),其中 A
和 B
都是非空有效括号字符串。
给出一个非空有效字符串 s
,考虑将其进行原语化分解,使得:s = P_1 + P_2 + ... + P_k
,其中 P_i
是有效括号字符串原语。
对 s
进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s
。
示例 1:
输入:s = "(()())(())" 输出:"()()()" 解释: 输入字符串为 "(()())(())",原语化分解得到 "(()())" + "(())", 删除每个部分中的最外层括号后得到 "()()" + "()" = "()()()"。
示例 2:
输入:s = "(()())(())(()(()))" 输出:"()()()()(())" 解释: 输入字符串为 "(()())(())(()(()))",原语化分解得到 "(()())" + "(())" + "(()(()))", 删除每个部分中的最外层括号后得到 "()()" + "()" + "()(())" = "()()()()(())"。
示例 3:
输入:s = "()()" 输出:"" 解释: 输入字符串为 "()()",原语化分解得到 "()" + "()", 删除每个部分中的最外层括号后得到 "" + "" = ""。
提示:
1 <= s.length <= 105
s[i]
为'('
或')'
s
是一个有效括号字符串