1.栈
操作受限的线性表,只能在一端插入和删除数据。
数组实现栈:顺序栈
链表实现栈:链式栈
#python实现顺序栈
class ArrayStack:
def __init__(self,n):
self.stack = []
self.limit = n
self.count = 0
def push(self,item):
if self.count == self.limit:
return False
self.stack.append(item)
self.count += 1
return True
def pop(self):
if self.stack:
return self.stack.pop()
self.count -= 1
else:
return None
def peek(self):
if self.stack:
return self.stack[-1]
def is_empty(self):
return not self.count
def size(self):
return self.count
出栈:O(1)
入栈:O(1)
栈满时需要复制到一个新的数组中,均摊时间复杂度下来也是O(1)
2.栈在函数调用中的应用
函数调用的临时变量储存在栈中,每进入一个函数。临时变量会作为栈帧储存在栈中,函数执行完,对应的栈帧出栈。
int main() {
int a = 1;
int ret = 0;
int res = 0;
ret = add(3, 5);
res = a + ret;
printf("%d", res);
reuturn 0;
}
int add(int x, int y) {
int sum = 0;
sum = x + y;
return sum;
}
为什么函数调用要用栈呢?
函数调用符合先进后出的特性。
3.栈在表达式求值中的应用
表达式求值一般是用两个栈,其中一个保存操作数的栈,另一个是保存运算符的栈。我们从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较。
如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取 2 个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。
但是在写代码的过程中,我发现,仅仅比较优先级,但是由于存储在栈中的依然是字符,还是要通过判断是否为‘±*/()’来做真正的运算,最后查了一下选择了以下这种用一个栈的方法。
主要思路:
将数字作为一个个的符号数字对,存储在栈中,遇到加减号可以直接存储,乘除号先计算再存储。
场景一,只计算“+”“-”“*”“/”和“ ”的式子
class Solution:
def calculate(self, s: str) -> int:
stack = []
sign = '+'
num = 0
for i in range(len(s)):
c = s[i]
if c.isdigit():
num = num * 10 + int(c)
//当这位不是数字且不是空格的时候,或者是最后一位数的时候,就要把num存入栈中
if (not c.isdigit() and c != ' ') or i+1 == len(s):
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign == '*':
stack[-1] = stack[-1]*num
elif sign == '/':
stack[-1] = int(stack[-1]/num)
num = 0
sign = c
return sum(stack)
场景2:计算存在‘(’ ‘)’的场景
class Solution:
def calculate(self,s):
num,i = self.helper(s)
return num
def helper(self,s):
stack = []
sign = '+'
num = 0
i = -1
while i < len(s)-1:
i = i+1
c = s[i]
if c.isdigit():
num = num*10+int(c)
if c == '(':
num,index = self.helper(s[i+1:])
i = i+index+1
if (not c.isdigit() and c!=' ') or i+1==len(s):
if sign=='+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign == '*':
stack[-1] = stack[-1]*num
elif sign == '/':
stack[-1] = int(stack[-1]/num)
num = 0
sign = c
if c == ')':
break
return sum(stack),i
from collections import deque
class Solution:
def calculate(self, s: str) -> int:
s = deque(s)
def helper(s):
stack = []
num = 0
sign = '+'
while len(s)>0:
c = s.popleft()
if c.isdigit():
num = num*10 + int(c)
if c =='(':
// 直接在s上pop,可以不用管返回的时候的i在什么位置
num = helper(s)
if (not c.isdigit() and c !=' ') or len(s)==0:
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign == '*':
stack[-1] = stack[-1]*num
elif sign == '/':
stack[-1] = int(stack[-1]/num)
num = 0
sign = c
if c == ')':
break
return sum(stack)
return helper(s)
4.栈在括号匹配中的应用
从左到右扫描字符串:
扫描到左括号时,入栈;
扫描到右括号时,从栈顶取出左括号,看是否匹配。
这里可以用字典来判断是否匹配:
dic = {’)’:’(’,’}’:’{’,’]’:’[’}
if dic[c] != cc
扫描中若遇到不匹配或者栈为空,则为非法格式。
字符串扫描完之后,还要检查栈是否为空!!
若不为空则还有未匹配的左括号,为非法格式。
class Solution:
def isValid(self, s: str) -> bool:
dic = {')':'(','}':'{',']':'['}
stack = []
for c in s:
if c in dic:
cc = stack.pop() if stack else '#'
if dic[c] != cc:
return False
else:
stack.append(c)
return not stack
5.浏览器的前进和后退
使用两个栈,X,Y
首次浏览的页面压入X栈
后退时先从X出栈,再将数据入栈Y
前进时从Y出栈,放入X中
如下图所示,顺序查看a,b,c三页面
回退到a页面:
前进到b页面:
查看d页面,此时c页面无法通过前进,后退查看,清空栈Y: