Leetcode刷题笔记之:栈

这里写目录标题

  • [155. 最小栈](https://leetcode-cn.com/problems/min-stack/)
  • [150. 逆波兰表达式求值](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
  • [394. 字符串解码](https://leetcode-cn.com/problems/decode-string/)
  • [133. 克隆图](https://leetcode-cn.com/problems/clone-graph/)
  • [200. 岛屿数量](https://leetcode-cn.com/problems/number-of-islands/)
  • 单调栈相关
    • [84. 柱状图中最大的矩形](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
    • [42. 接雨水](https://leetcode-cn.com/problems/trapping-rain-water/)
    • [739. 每日温度](https://leetcode-cn.com/problems/daily-temperatures/)

155. 最小栈

设计一个可以在O(1)时间返回栈中最小值的栈。实现方式append进栈顶的同时连同当前最小的元素一起以元组形式存入,这样每次取最小值只需要取栈顶的元组即可

class MinStack:

    def __init__(self):
        # 存放元组(cur_element, min_element)
        self.stack = []

    def push(self, x: int) -> None:
        if len(self.stack) == 0:
            self.stack.append((x, x))
        else:
            min_int = min(self.stack[-1][1], x)
            self.stack.append((x, min_int))

    def pop(self) -> None:
        if self.stack:
            self.stack.pop()[0]

    def top(self) -> int:
        if self.stack:
            return self.stack[-1][0]

    def getMin(self) -> int:
        return self.stack[-1][1]

150. 逆波兰表达式求值

这道题很简单,就是将token一个一个推入栈中即可,遇到运算符就推出栈顶两个元素进行运算即可,注意,计算结果有可能被作为之后运算的元素,所以结果要添加进栈中(利用栈保存结果)

关键字:利用栈保存结果

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        def compute(int1, int2, token):
            if token == "+":
                return int1 + int2
            elif token == "-":
                return int1 - int2
            elif token == "*":
                return int1 * int2
            elif token == "/":
                abs_result = abs(int1) // abs(int2)
                return abs_result if int1 * int2 > 0 else -abs_result
                
        stack = []
        token_list = ["+", "-", "*", "/"]
        for token in tokens:
            if token in token_list:
                # 注意这里, 栈顶推出的是第二运算数
                int2 = stack.pop()
                int1 = stack.pop()
                result = compute(int1, int2, token)
                stack.append(result)
            else:
                stack.append(int(token))
        return stack.pop()

394. 字符串解码

class Solution:
    def decodeString(self, s: str) -> str:
        # 存放数字和字符创
        int_stack = []
        str_stack = ['']
        repeat = 0
        for t in s:
            # t为数字
            if "0" <= t <= '9':
                repeat = repeat * 10 + int(t)
            elif t == "[":
                int_stack.append(repeat)
                str_stack.append('')
                repeat = 0
            elif t == "]":
                temp_str = str_stack.pop()
                str_stack[-1] += temp_str * int_stack.pop()
            else:
                str_stack[-1] += t
        return str_stack.pop()

133. 克隆图

构建一个node映射copyNode的哈希表,同时利用栈来保存中间过程。

我们可以发现,当涉及构件图,点对某item的映射表通常是必须要设置的,这个很重要

# BFS
class Solution:
    def cloneGraph(self, start: 'Node') -> 'Node':
        
        if start is None:
            return None
        
        node_to_copy = {start : Node(start.val)}
        queue = [start]
        while queue:
            cur = queue.pop(0)
            curr_copy = node_to_copy[cur]
            for n in cur.neighbors:
                if n not in node_to_copy:
                    node_to_copy[n] = Node(n.val)
                    queue.append(n)
                curr_copy.neighbors.append(node_to_copy[n])
        return node_to_copy[start]
                

200. 岛屿数量

岛屿类的题目,都可以用类似的思路。利用深度/广度遍历所有可能性,注意访问过的点需要被标记以避免重复访问。

# BFS
class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        h = len(grid)
        w = len(grid[0])

        def getNeighbours(i,j):
            result = []
            for d in [(-1,0), (1,0), (0,-1), (0,1)]:
                x,y = i+d[0], j+d[1]
                # 邻点在图内且为未被访问过
                if h > x >= 0 and w > y >= 0:
                    if grid[x][y] == '1':
                        result.append((x,y))
                        grid[x][y] = '2'
            return result

        # BFS
        # bad case
        if not grid:
            return
        bfs_count = 0
        for i in range(h):
            for j in range(w):
                if grid[i][j] == '1':
                    bfs_count += 1
                    stack = [(i, j)]
                    grid[i][j] = '2'
                    while stack:
                        cur = stack.pop(0)
                        # 遍历过的1改为2,意为已经访问过
                        stack += getNeighbours(cur[0], cur[1])
                    
        return bfs_count

单调栈相关

84. 柱状图中最大的矩形

暴力解法,遍历每个柱子,求出每个柱子可以向左右延伸的宽度

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        max_area = 0
        for i, h in enumerate(heights):
            left, right = i-1, i+1
            while left >= 0 and heights[left] >= h:
                left -= 1
            while right < len(heights) and heights[right] >= h:
                right += 1
            width = right - left - 1
            if h * width >= max_area:
                max_area = h * width
        return max_area

单调栈法:

单调栈是一个存储的元素递增或者递减的的栈结构。此题思路为:

由暴力算法我们可以看到,无非就是遍历每个柱子,对每个柱子我们向左向右寻找可以完全覆盖这个柱子的矩形的最大边界。

什么时候找到边界?边界对应的柱子高度小于当前柱子的时候。

思路类似于:

从左到右的几块木板,我希望木板越来越长,如果遇到了短的木板,我就截掉他的前一块最长的木板到与第二长的木板同长。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        if not heights:
            return 0
        
        size = len(heights)
        stack = [] # 单调栈,递增
        max_res = 0
        for i in range(size):
            # 我们要寻找能够覆盖柱子的最大矩形
            # 当前柱子小于上一个柱子,这意味着上一个柱子的最大矩形右边界到头了(i)
            while len(stack) > 0 and heights[i] < heights[stack[-1]]:
                # 这个弹出的上一个柱子就是我们要focus on 的柱子
                cur_h = heights[stack.pop()]
                # 这里是应对相邻柱子同高的情况,相当于把左边界左移
                while len(stack) > 0 and cur_h == heights[stack[-1]]:
                    stack.pop()
                # 计算宽度,右边界就是i,左边界就是新栈顶(或者0)
                # 为什么是新栈顶?
                # 因为stack是单调栈,所以新栈顶一定是离 弹出的旧栈顶 最近 且小于 它 的柱子
                # 想想看这是不是就是右边界的意思?
                if len(stack) > 0:
                    cur_w = i - stack[-1] -1
                else:
                    cur_w = i
                max_res = max(max_res, cur_w * cur_h)

            stack.append(i)
        
        
        # 遍历完毕后,栈里面还留有左边界不确定的柱子,它们的右边界就是最右边(size)
        while len(stack) > 0:
            cur_h = heights[stack.pop()]
            while len(stack) > 0 and cur_h == heights[stack[-1]]:
                stack.pop()
            if len(stack) > 0:
                cur_w = size - stack[-1] -1
            else:
                cur_w = size
            max_res = max(max_res, cur_w * cur_h)
        return max_res

42. 接雨水

如题,我们要有一个柱子高度先递减再递增的凹坑结构才能储存雨水,这里我们就可以引入递减的单调栈。

我们从左到右遍历柱子,高度小于栈顶的柱子就放进栈内,知道遇到第一个不再递减的柱子 i i i ,这个时候我们排出栈顶,先把栈顶所代表的凹坑可以存储的雨水加到总结果里,无疑,这里的凹坑左边界是新栈顶,右边界 i i i

继续遍历,如果又递减了,那就重复上面过程,如果仍然在递增,那说明仍能继续排水,同样排出栈顶(这个时候的排出栈顶起到了将上面已经计算过的凹坑填平的作用),继续上面过程。

class Solution:
    def trap(self, height: List[int]) -> int:    
        rain = 0
        stack = []
        size = len(height) 
        for i in range(size):
            while stack and (height[stack[-1]] < height[i]):
                top = stack.pop()
                if len(stack) == 0:
                    break
                width = i - stack[-1] - 1
                bd_height = min(height[i], height[stack[-1]]) - height[top] 
                rain += (width * bd_height)
            stack.append(i)
        return rain    

739. 每日温度

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        if not T:
            return []
        result = []
        for i, t in enumerate(T):
            for j in range(i, len(T)):
                if T[j] > t:
                    result.append(j-i)
                    break
                if j == len(T) - 1:
                    result.append(0)
        return result
class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        if not T:
            return []
        result = {}
        stack = []
        size = len(T)
        for i in range(size):
            while len(stack) > 0 and T[i] > T[stack[-1]]:
                top = stack.pop()
                result[top] = i - top
            stack.append(i)
        while stack:
            top = stack.pop()
            result[top] = 0
        return [result[x] for x in range(len(T))]

你可能感兴趣的:(Leetcode,算法,leetcode,算法,数据结构)