为什么要了解数据结构及算法?有个很恰当的比喻,如果把编程比作习武,会用哪种计算机语言相当于学会了招式,而学习算法则相当于在修炼内功。算法能帮我们优化程序占用的空间或消耗的时间,提高我们解决问题的效率。
def stack_apply(st:str):
#用字典的方式来表示左右括号的对应关系
bracket = {'(':')', '[':']', '{':'}'}
li = []
for item in st:
#遇到左括号就进栈
if item in bracket.keys():
li.append(item)
#当栈空了还剩一个右括号,表示错误匹配,返回False
elif len(li)==0:
return False
#遇右括号而且和栈顶的左括号相匹配,就让左括号出栈
elif item == bracket[li[-1]]:
li.pop()
#遇到了右括号,但是和栈顶的左括号不匹配,直接返回False
else:
return False
#最后判断一下栈空了没有,空了就正确匹配,否则没有正确匹配
if len(li)==0:
return True
else:
return False
print(stack_apply('[]{([]])}'))
print(stack_apply('([)]'))
print(stack_apply('[])'))
print(stack_apply('{[]'))
print(stack_apply('{[()]}'))
from collections import deque
deque(open('test.txt','r'),5)
class queue_using_stack:
def __init__(self):
#初始化两个栈
self.stack1 = []
self.stack2 = []
#进队即1号栈进栈
def append(self,key):
self.stack1.append(key)
def pop(self):
#如果2号栈空,则先把1号栈里的元素依次出栈进2号栈
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
#2号栈不空时,出队即2号栈出栈
return self.stack2.pop()
在讲解深度优先和广度优先之前,需要引入另一个栈和队列的应用问题,迷宫问题:
maze = [ #迷宫地图,1代表墙壁,0代表通路
[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]
]
我们用上述二维数组表示一个迷宫地图,其中1代表墙壁,0代表通路,尝试找到从起点(1,1)到终点(8.8)的可行路径。解决这个迷宫问题的两种思路,深度优先和广度优先,对应的就是栈和队列的应用。
#用一个数组表示四个行走方向
dirs = [
lambda x,y:(x-1,y), #左
lambda x,y:(x+1,y), #右
lambda x,y:(x,y-1), #上
lambda x,y:(x,y+1), #下
]
#x1,y1表示起点,x2,y2表示终点
def solve_maze_stack(x1, y1, x2, y2):
#用一个栈来存储行走路径,每走一步就进栈一个位置点
stack = []
#用数字2表示已经走过的路,把起点先标识为已走过
maze[x1][y1] = 2
#起点先进栈
stack.append((x1,y1))
while len(stack)>0:
#判断如果栈顶就是终点,表示走通了,打印栈里的元素即表示路径
if stack[-1] == (x2, y2):
print(stack)
return True
#遍历四个方向
for f in dirs:
#取出该方向的下一步位置
cur_point = maze[f(*stack[-1])[0]][f(*stack[-1])[1]]
#如果是没走过的通路
if cur_point==0:
#则标记为已走过并进栈
maze[f(*stack[-1])[0]][f(*stack[-1])[1]] = 2
stack.append(f(*stack[-1]))
break
#这个else是和for对应的,表示四个方向都无路可走了,就出栈最后一个位置节点
else:
stack.pop()
#栈空了仍未碰到终点,表示没有可行路,返回False
return False
solve_maze_stack(1,1,8,8)
这种思路即是深度优先,这样找出的路径不能保证是最短路径。
from collections import deque
def solve_maze_queue(x1, y1, x2, y2):
#创建一个队列,用来存每个岔路的最新位置点
q = deque()
#起点用2标记为已经走过
maze[x1][y1] = 2
#起点先进队,并用tuple中最后一个值记录上一步在trace数组中的index位置,方便回溯路径,起点用-1表示
q.append((x1,y1,-1))
#创建一个trace数组用来记录路径的
trace = []
#队不空时循环
while len(q)>0:
#出队队头位置点并记录为当前节点
cur_node = q.popleft()
#把当前节点记在路径trace里
trace.append(cur_node)
#判断如果走到了终点
if cur_node[:2] == (x2, y2):
#从trace的最后一个元素(即终点)开始
n = len(trace)-1
#遍历直至到起点(起点时n为-1)
while n>=0:
#打印路径位置点
print(trace[n])
#通过tuple中最后一个元素,追溯到上一步的位置点
n = trace[n][-1]
return True
#遍历四个方向
for f in dirs:
#获取到下一步位置点
next_node = f(*cur_node[:2])
#只要有路可走,就把每个可行路径的下个位置点入队,并把该位置点标记为2(已走过)
if maze[next_node[0]][next_node[1]]==0:
maze[next_node[0]][next_node[1]] = 2
q.append((next_node[0],next_node[1],len(trace)-1))
#队空了仍未到终点,表示没有通路,返回False
return False
solve_maze_queue(1,1,8,8)
广度优先搜索可以保证最终找到的路径一定是最短路径。
当然不仅仅是迷宫问题,基本所有问题用栈去解决即代表深度优先,用队列去解决即代表广度优先,比如二叉树的遍历,我们后面的文章会提到。
**内容整理自网络课程算法与数据结构LeetCode编程