栈和队列思想

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值