1.进制转换
- 将十进制转换成其他进制
- 进制转换原理:计算机基础进制转换(二进制、八进制、十进制、十六进制)
- 算法原理:利用栈后进先出的特点,将每次的余数作为字符串放入栈中,除完后再依次出栈相连
- 难度:★★
- 易错点:转换成十六进制时大于10的部分要特殊处理
- 代码示例:
# 十进制转换为其他进制
def ten_change(num, scale): # num为传入的十进制数字,scale为要转换成的进制
zhan = list() # 创建一个栈
changeNum = '' # 存放转换后的数字
digits = "0123456789ABCDEF" #用来防止>10的数字无法表示
while num > 0: # 入栈
zhan.append(str(num % scale))
num = num // scale
# print(zhan) # 用于检验
while zhan != []:
changeNum += digits[int(zhan.pop())] #出栈
return changeNum
if __name__ == '__main__':
num = int(input("请输入一个十进制数字:"))
scale = int(input("请输入要转换成的进制数(包括2、8、16):"))
print(ten_change(num, scale))
- 运行结果
请输入一个十进制数字:9999
请输入要转换成的进制数(包括2、8、16):16
270F
2.括号匹配
- 括号使用正确的句子返回True,不正确的返回False
- 算法原理:
- 利用循环,字符串中的字符一个一个进行检测,检测到括号以外的东西跳过。
- 利用栈后进先出的原理,检测到左括号则入栈,检测到右括号,则栈中最后进入的左括号出栈,与之进行匹配检测。
- 匹配正确则continue,错误则返回False。全部循环结束且不出错则返回True。
- 难度:★★★
- 易错点:理清楚步骤和细节
- 代码示例:
def match_parentheses(s):
ls = [] # 将list当作一个栈使用
parentheses = "()[]{}()【】「」"
left_parentheses = "([{(【「" # 左括号
right_parentheses = ")]})】」" # 右括号
for i in range(0, len(s)):
si = s[i]
#如果不是括号则continue
if parentheses.find(si) == -1:
continue
# 左括号入栈
if left_parentheses.find(si) != -1:
ls.append(si)
continue
if len(ls) == 0:
return False
# 检测到是右括号,出栈匹配
if right_parentheses.find(si) != -1:
p = ls.pop() #最后入栈的左括号
if (p == '(' and si == ')') or (p == '[' and si == ']') or (p == '{' and si == '}') or\
(p == '(' and si == ')') or (p == '【' and si == '】') or (p == '「' and si == '」'):
continue
else:
return False
#检测是否有多余括号
if len(ls) > 0:
return False
return True
if __name__ == '__main__':
s = input("请输入要匹配的句子:")
result = match_parentheses(s)
print(s, result)
- 运行结果
请输入要匹配的句子:{{[ddd(da)]}}
{{ddd(da)}} True
请输入要匹配的句子:(shdhd){sk{sjs}
(shdhd){sk{sjs} False
3.迷宫求解
- 找出迷宫的出路
- 算法原理:
- 将迷宫数字化,python中利用list嵌套形成二维list。0表示能通过,1表示不能通过,将经过的位置标记为2
- pos为当前的位置坐标,(x,y),pos[0]为纵(竖着的)坐标,pos[1]为横坐标
- 每次的位置和方向((x,y), nxt)作为一个序列入栈,初始为((1,1), 0)
- 每次位置以向右为初始方向,按照(0右 1下 2左 3上)的顺序探索下一位置,当有位置走到死路时。进行返回,用pop()方法取出上一步(后入栈)的坐标和方向,在这个坐标上继续循环探索其他方向的可能性(
比如上次为((7, 10),1)表示上次在(7,10)处向下进行探索但是失败了,则在该点继续向左(2)探索,成功则继续,不成功则向上(3)探索,若还不成功则跳出内层循环,继续往后退一步)
其实这里不是很严谨,在最下面有一个思考- 直到终点(10,12),结束并打印栈中所有的路径。
- 难度:★★★★★★★★
- 易错点:因为难理解所以易错
from pythonds.basic.stack import Stack
def mark(maze,pos): # 给迷宫maze的位置pos标"2"表示“到过了”
maze[pos[0]][pos[1]]=2
def passable(maze,pos): # 检查迷宫maze的位置pos是否可通行
return maze[pos[0]][pos[1]]==0 # 0能通过,返回True,不能返回False
def maze_solver(maze,start,end):
if start==end:
print(start)
return
st=Stack() # 实例化一个栈为st
mark(maze,start)
st.push((start,0)) #入口和方向0(向右)的序对入栈
while not st.isEmpty(): #走不通时回退,只要栈里有坐标就可以一直回退,回退到没有坐标说明迷宫走不通
pos,nxt=st.pop() #取栈顶与检查方向,pos为位置,nxt为检查方向
for i in range(nxt,4):
#依次检查未检查的方向,算出下一位置 0右 1下 2左 3上
#nextp为下一步的坐标,用if语句分为下一步是终点或可行点,都不是则继续循环探索其他方向
nextp = (pos[0] + dirs[i][0], pos[1] + dirs[i][1])
if nextp==end:
st.push((pos, i)) #到达出口,打印位置
st.push(end)
for m in range(st.size()): # 打印走过的点以及方向
print(st.items[m])
return
if passable(maze, nextp): #遇到未探索的新位置,0可通过,1和2不行
st.push((pos,i)) #记录该点(前行前的位置),放入栈
mark(maze,nextp) #标记已走过点的位置为2
st.push((nextp,0)) #新位置(前行后的位置)入栈,初始方向向右
break #退出内层循环,下次迭代将以新栈顶作为当前位置继续
print("找不到路径")
if __name__ == '__main__':
dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 0右 1下 2左 3上
maze=[[1,1,1,1,1,1,1,1,1,1,1,1,1,1],\
[1,0,0,0,1,1,0,0,0,1,0,0,0,1],\
[1,0,1,0,0,0,0,1,0,1,0,1,0,1],\
[1,0,1,0,1,1,1,1,0,1,0,1,0,1],\
[1,0,1,0,0,0,0,0,0,1,1,1,0,1],\
[1,0,1,1,1,1,1,1,1,1,0,0,0,1],\
[1,0,1,0,0,0,0,0,0,0,0,1,0,1],\
[1,0,0,0,1,1,1,0,1,0,1,1,0,1],\
[1,0,1,0,1,0,1,0,1,0,1,0,0,1],\
[1,0,1,0,1,0,1,0,1,1,1,1,0,1],\
[1,0,1,0,0,0,1,0,0,1,0,0,0,1],\
[1,1,1,1,1,1,1,1,1,1,1,1,1,1]]
start=(1,1)
end=(10,12)
maze_solver(maze,start,end)
- 运行结果
((1, 1), 1)
((2, 1), 1)
((3, 1), 1)
((4, 1), 1)
((5, 1), 1)
((6, 1), 1)
((7, 1), 0)
((7, 2), 0)
((7, 3), 3)
((6, 3), 0)
((6, 4), 0)
((6, 5), 0)
((6, 6), 0)
((6, 7), 0)
((6, 8), 0)
((6, 9), 0)
((6, 10), 3)
((5, 10), 0)
((5, 11), 0)
((5, 12), 1)
((6, 12), 1)
((7, 12), 1)
((8, 12), 1)
((9, 12), 1)
(10, 12)
-
思考
- 其实有一个困扰了我四十分钟的问题,就是为什么不是
for i in range(nxt+1,4):
而不是for i in range(nxt,4):
。因为我第一次写的时候觉得,每次碰壁无路可走时开始会退,应该可以直接按照上次的检查方向按顺序继续检查就行(比如上个点是往右走的,那么退回到上个点之后进行往下、往左、往右的探索就行了),这是nxt+1的想法。- 但我们+1之后发现运行不了,因为其实当最后入栈的是新的坐标时,初始的方向是0,对于这个新坐标确实是要通过0(向右)来开始探索的,+1的话就会从1(向下)开始探索。我们在内循环中加一个
print(i)
其实就可以看出来1 1 1 1 1 1 1 1 1 1 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3
当pos一直往下走碰壁之后,就一直往回(向上)去了,正常的新坐标是从1开始探索的而不是0
- 但不+1的情况会在退回错误的步骤时还会多一次毫无意义的检查,显得很笨
- 同时,如果使用
print(i)
会发现程序非常的呆。0 0 0 1 0 0 0 0 1 2 3 0 0 0 1 0 1 0 1 /0 1 2 0 1 2 0 1 2 0 1 2 0 1 2/ 0 1 2 3 0 1 2 3 3 2 3 2 3 2 3 2 3 2 3 1 2 3 1 2 3 1 2 3 0 1 2 3 0 等等很长
- 程序会先往右探索,然后错了一直返回,但最后是可以通过的。我能力和精力有限,可能在每个岔口进行一个多线程的运行,会让程序聪明很多。