1. 利用栈思想判断给定的一串括号是否左右匹配。
思路:如果字符是左括号,则存入栈,如果是右括号,判断栈顶括号是否与它匹配,匹配的话弹出,如果不匹配或者遍历完括号串后栈不为空,说明该串括号不是左右匹配。
def bracket_match(str_s):
if not str_s:
return None
dict_match = {'(':')','[':']','{':'}'}
bracket = []
for ch in str_s:
if ch in dict_match:
bracket.append(ch)
else:
if dict_match[bracket[-1]] == ch:
bracket.pop()
else:
return False
if not bracket:
return True
else:
return False
str_s = input("Please input a brackets string:")
print(bracket_match(str_s))
2. 利用栈求解迷宫问题(深度优先遍历,也叫回溯法):
给定一个由0和1组成的矩阵,给定起点和终点坐标,矩阵中1代表无法通过,求从起点走到终点的路径。
思路:将起点存入栈内,按照上下左右的顺序查看起点位置四个方向是否可行,如果可行,将该位置坐标存入栈内,继续查看该位置的四个方向是否可行。如果一个位置的四个方向都无法通行,则回退到上一个位置,继续查找其他方向是否可行。总结起来一句话就是:一条道走到黑,不撞南墙不回头,撞了南墙后就回退到上一个位置继续找一条路走到黑。
maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
direction = [
lambda x,y: (x,y-1),
lambda x,y: (x+1,y),
lambda x,y: (x,y+1),
lambda x,y: (x-1,y)
]
def maze_path_depth(x1,y1,x2,y2):
stack_path = [(x1,y1)]
while stack_path:
if stack_path[-1][0] == x2 and stack_path[-1][1] == y2: # 如果找到出口,输出路径
for pos in stack_path:
print(pos, end=" ")
return
for dir in direction: # 查找四个方向是否可行
next_pos = dir(stack_path[-1][0],stack_path[-1][1])
if maze[next_pos[0]][next_pos[1]] == 0:
stack_path.append(next_pos)
maze[next_pos[0]][next_pos[1]] = -1 # 经过的位置要变为-1,防止再次经过
break
else:
maze[next_pos[0]][next_pos[1]] = -1
stack_path.pop() # 如果四个方向都无法通行,回退到上一个位置坐标
else:
return None
maze_path_depth(1,1,8,8)
3. 利用队列求解迷宫问题(广度优先遍历):
思路:将起点存入队列,然后寻找四个方向是否可行,将可行的位置都存入队列。下次从队首取位置,接着将该位置四个方向可行的位置存入队列。这样可保证找到起点到终点的最短路径。
为了重建最短路径,除了位置后坐标外,还加入了num值,用来表示该位置是从哪个位置走过来的。
maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
direction = [
lambda x,y: (x-1,y),
lambda x,y: (x,y+1),
lambda x,y: (x+1,y),
lambda x,y: (x,y-1)
]
def maze_path_extend(x1,y1,x2,y2):
stack_path = [(x1,y1,-1)] # 第三个元素表示当前这个位置是从path中的第几个元素代表的位置过来的
path = [] # 因为stack_path每次都要pop,因此,用path来存储经过的位置
num = -1 # 用来表示当前位置是从path中的第num个元素代表的位置过来的。
maze[x1][y1] = -1
while stack_path:
cur_pos = stack_path.pop(0)
path.append(cur_pos) # 每次都要存储经过的位置
num += 1
if cur_pos[0] == x2 and cur_pos[1] == y2:
real_path = []
while cur_pos[2] != -1: # 当num = -1的时候,代表已经回溯到起点了
real_path.append((cur_pos[0],cur_pos[1]))
cur_pos = path[cur_pos[2]] # 根据num的值,找到该位置是从哪个位置过来的
real_path.append((cur_pos[0], cur_pos[1])) # 存储从起点到终点路径
real_path.reverse()
for pos in real_path:
print(pos,end=' ')
return True
for dir in direction: # 查找四个方向是否可行
next_pos = dir(cur_pos[0],cur_pos[1])
if maze[next_pos[0]][next_pos[1]] == 0:
stack_path.append((next_pos[0],next_pos[1],num))
maze[next_pos[0]][next_pos[1]] = -1 # 经过的位置要变为-1,防止再次经过
else:
return None
if __name__ == '__main__':
print(maze_path_extend(1,1,8,8))
4. 单词搜索问题:
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
思路:利用深度优先遍历的方法,遍历board,找到word中的第一个字母时,则从该位置开始上、右、下、左搜索下一个字母,并把当前位置存入栈中,如果在当前位置没有找到下一个字母,则弹出栈顶位置,从新栈顶位置开始搜索。需要注意的是,从新栈顶位置搜索时,要接着该位置之前搜索过的下一个位置搜索,因此需要定义一个数组pre_dir用来存储当前位置遍历过的四个方向的位置。
class Solution:
def exist(self, board, word: str) -> bool:
def dfs(pos,board,word):
dirs = [lambda x,y: (x-1,y),
lambda x,y: (x,y+1),
lambda x,y: (x+1,y),
lambda x,y: (x,y-1)]
temp = [[x for x in row] for row in board]
position = [pos]
pre_dir = [0] # 用来存储当前位置遍历过的dirs,下次再遍历时可以接着遍历,无需从dirs中的一开始遍历
ind = 1 # word的坐标
while position:
cur_pos = position[-1]
for i in range(pre_dir[-1],len(dirs)):
di = dirs[i]
nex_pos = di(cur_pos[0],cur_pos[1])
if 0 <= nex_pos[0] < len(temp) and\
0 <= nex_pos[1] < len(temp[0]) and\
temp[nex_pos[0]][nex_pos[1]] == word[ind]:
position.append(nex_pos)
pre_dir[-1] = i+1 # 将当前位置该遍历的下一个dirs存入栈中
pre_dir.append(0) # 下一个位置从dirs的一开始遍历
temp[cur_pos[0]][cur_pos[1]] = -1 # 将遍历过的位置设置与-1
ind += 1
if ind == len(word):
return True
break
else: # 当前位置四个方向都没找到下一个字母
pre_dir.pop() # 下一个位置接着之前的dirs继续遍历
ind -= 1
pre_pos = position.pop() # 将弹出的位置再设置为原有的字母
temp[pre_pos[0]][pre_pos[1]] = word[ind]
else:
return False
for i in range(len(board)): # 遍历board,寻找与word第一个字母相当的位置
for j in range(len(board[0])):
if board[i][j] == word[0]:
if len(word) == 1:
return True
else:
pos = (i,j)
if dfs(pos,board,word):
return True
else:
return False
board = [["C","A","A"],["A","A","A"],["B","C","D"]]
word = "AAB"
x = Solution()
print(x.exist(board,word))
5. 接雨水问题:给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
思路:利用栈的思想来求解。
1. 定义一个栈:h_arr来存储高度,当栈为空是,把当前高度(非零)存入栈中。
2. 当前高度大于等于栈顶高度时,计算两个高度之间的存水量,并将栈顶元素pop,继续比较下一个栈顶元素。
3.当前高度小于栈顶高度时,计算两个高度之间的存水量,并将该高度存入栈中。
class Solution:
def trap(self, height) -> int:
res = 0
h_arr = [] # 存储高度
for i,h in enumerate(height): # 遍历每一个高度与其对应的坐标
if h:
while h_arr:
if h_arr[-1][0] <= h: # 当当前高度大于等于栈顶高度时,计算两个高度之间的的存水量,并将栈顶元素pop,继续比较下一个栈顶元素
if (i-1) > h_arr[-1][1]:
hei = h_arr[-1][0] - max(height[h_arr[-1][1]+1:i]) # 计算当前两个高度的坐标之间是否存在其他较低的高度,存在的话需减去。
length = i-1-h_arr[-1][1]
res += (hei*length)
h_arr.pop()
else: # 当当前高度小于栈顶高度时,计算两个高度之间的存水量,并将该高度存入栈中
if (i - 1) > h_arr[-1][1]:
hei = h - max(height[h_arr[-1][1]+1:i]) # 计算当前两个高度的坐标之间是否存在其他较低的高度,存在的话需减去。
length = i-1-h_arr[-1][1]
res += (hei*length)
h_arr.append([h,i])
break
else:
h_arr.append([h,i]) # 当栈为空时,存入当前高度值及其对应的坐标
return res
x = Solution()
print(x.trap([0,1,0,4,0,2,0,1,3,0,5,0,2,1,0]))
6. 柱状图中最大的矩形:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10
思路:定义一个栈来存储高度,当高度大于等于栈顶元素时,入栈。当小于栈顶元素时,计算此时栈顶元素所能取得的最大面积。将该栈顶元素弹出,继续计算新栈顶元素。当新栈顶元素小于当前高度时,将该高度入栈。遍历完所有高度后,对栈中的储存元素从栈顶开始计算每一个高度能取得的最大值。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
if not heights:
return 0
stack = [] # 用来存储高度
max_val = 0
for i,h in enumerate(heights):
if not stack: # 如果栈为空,将高度入栈
stack.append([h,i])
continue
elif h < stack[-1][0]: # 如果当前高度小于栈顶元素
while h < stack[-1][0]:
pop_h = stack.pop() # 计算栈顶高度所能取得的最大面积
if not stack: # 如果当前栈中只有一个元素,则该元素的宽为i
max_val = max(max_val,i*pop_h[0])
break
else: # 如果栈中还有元素,则宽为新栈顶元素的坐标到i
max_val = max(max_val,(i-stack[-1][1]-1)*pop_h[0])
stack.append([h,i])
while stack:
pop_h = stack.pop() # 对栈中剩余的高度计算面积
if stack:
max_val = max(max_val,(len(heights)-stack[-1][1]-1)*pop_h[0])
# 当栈中还有元素时,则宽为新栈顶元素的坐标到最后(整个数组的长度)
else:
max_val = max(max_val,len(heights)*pop_h[0])
# 当栈中没有元素时,说明该高度为整个数组中的最小值。则宽为整个数组的长度
return max_val