数据结构-栈的练习(用python实现进制转换、括号匹配、迷宫求解)

1.进制转换

# 十进制转换为其他进制
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
  • 算法原理:
  1. 利用循环,字符串中的字符一个一个进行检测,检测到括号以外的东西跳过。
  2. 利用栈后进先出的原理,检测到左括号则入栈,检测到右括号,则栈中最后进入的左括号出栈,与之进行匹配检测。
  3. 匹配正确则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.迷宫求解

  • 找出迷宫的出路
    迷宫求解
  • 算法原理:
  1. 将迷宫数字化,python中利用list嵌套形成二维list。0表示能通过,1表示不能通过,将经过的位置标记为2
  2. pos为当前的位置坐标,(x,y),pos[0]为纵(竖着的)坐标,pos[1]为横坐标
  3. 每次的位置和方向((x,y), nxt)作为一个序列入栈,初始为((1,1), 0)
  4. 每次位置以向右为初始方向,按照(0右 1下 2左 3上)的顺序探索下一位置,当有位置走到死路时。进行返回,用pop()方法取出上一步(后入栈)的坐标和方向,在这个坐标上继续循环探索其他方向的可能性(比如上次为((7, 10),1)表示上次在(7,10)处向下进行探索但是失败了,则在该点继续向左(2)探索,成功则继续,不成功则向上(3)探索,若还不成功则跳出内层循环,继续往后退一步) 其实这里不是很严谨,在最下面有一个思考
  5. 直到终点(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)
  1. 其实有一个困扰了我四十分钟的问题,就是为什么不是for i in range(nxt+1,4):而不是for i in range(nxt,4):。因为我第一次写的时候觉得,每次碰壁无路可走时开始会退,应该可以直接按照上次的检查方向按顺序继续检查就行(比如上个点是往右走的,那么退回到上个点之后进行往下、往左、往右的探索就行了),这是nxt+1的想法。
  2. 但我们+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. 但不+1的情况会在退回错误的步骤时还会多一次毫无意义的检查,显得很笨
  2. 同时,如果使用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 等等很长

  1. 程序会先往右探索,然后错了一直返回,但最后是可以通过的。我能力和精力有限,可能在每个岔口进行一个多线程的运行,会让程序聪明很多。
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丸丸丸子w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值