秋招 hot 100刷题记录【5】

1.路径总和III
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def pathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: int
        """
        # 思路:前缀和+哈希
        # 二叉树前缀和相当于从根结点开始的路径和
        # 1.前缀和数组中 s[0] = 0 —— 要把任意路径和都表示成两个前缀和的差 必须增加0
		# 时间复杂度 o(N) 空间复杂度 o(N)
        global ans 
        ans = 0
        cnt = defaultdict(int) # 初始化哈希表
        cnt[0] = 1 #  前缀和数组 s[0] = 1 空路径数量为1

        # 二叉树遍历 用深度优先
        def dfs(node, s):
            if not node: return node # 空节点返回
            s += node.val # 计算前缀和
            global ans # 申明全局变量
            ans += cnt[s - targetSum] # 先更新结果 再更新数据 否则当targetSum=0的时候 会导致多计算一条数据
            cnt[s] += 1 #  前缀和为s的路径增加一条
            # 递归访问当前节点的左右子节点
            dfs(node.left, s)
            dfs(node.right, s)
            # 完成当前节点的处理后,进行回溯,即将当前前缀和的计数减1
            cnt[s] -= 1 # 恢复现场
            # 如果不恢复现场,当我们递归完左子树,要递归右子树时,cnt 中还保存着左子树的数据。
            # 但递归到右子树,要计算的路径并不涉及到左子树的任何节点,如果不恢复现场,cnt 中统计的前缀和个数会更多,我们算出来的答案可能比正确答案更大。
        
        dfs(root, 0)
        return ans

2.二叉树的最近公共祖先
  • 代码链接
  • 时间复杂度:O(n),其中 n 为二叉树的节点个数。
    空间复杂度:O(n)。最坏情况下,二叉树是一条链,因此递归需要 O(n) 的栈空间。
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        # 1.递归实现 判断返回值应该为树的节点 就是最近的公共祖先
        # 2.判断递归终止条件 就是什么时候开始返回
        # 几种情况 首先是(1)root为空 返回None就错误了 而是返回节点
        #              (2)找到了p或者q,注意p,q不是一样的值 只要返回该节点就行

        if not root: return root
        elif root == p: return root
        elif root == q: return root
        # 后序遍历需要对返回值做进一步的逻辑处理 (从下向上处理就需要后序遍历)
        # 因此需要用left和right来进行存储

        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        
        # 只有右子树找到 返回右子树结果
        if not left and right:
            return right
        # 只有左子树找到 返回左子树结果
        elif not right and left:
            return left
        # 左右子树都没找到 返回空节点
        elif not left and not right:
            return left
        # 左右子树都找到 返回当前结果
        else: 
            return root
3.二叉树中的最大路径和
  • 代码链接
  • 链:从叶子到当前节点的路径。其节点值之和是 dfs 的返回值。
  • 直径:等价于由两条(或者一条)链拼成的路径。我们枚举每个 node,假设直径在这里「拐弯」,也就是计算由左右两条从叶子到 node 的链的节点值之和,去更新答案的最大值。
    ⚠注意:dfs 返回的是链的节点值之和,不是直径的节点值之和。
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def maxPathSum(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        global ans
        ans = float("-inf")
        def dfs(node):
            if node is None:
                return 0
            l_val = dfs(node.left)
            r_val = dfs(node.right)
            global ans
            ans = max(ans, l_val + r_val + node.val) # 最大路径和要包括左右子链长度 + 本身结点值
            # 注意 深度遍历的时候返回的是 链长 而不是路径长
            # 此外由于要找最大子路径 返回的左右子链长<0 的时候直接截断
            return max(max(l_val, r_val) + node.val, 0)
        dfs(root)
        return ans
4.岛屿数量
class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        # 思路:
        # 如果遇到格子为1的岛屿就以该岛屿为中心开始深度搜索,将可以访问的岛屿都置为其余数字 并且count + 1
        def dfs(grid, i, j):
            # 如果越界了则返回
            if not 0 <= i < len(grid) or not 0<= j < len(grid[0]): return
            # 如果此时遇到了海洋则返回
            elif grid[i][j] == '0':
                return
            # !!! 修改当前访问到节点的值
            grid[i][j] = '0'

            # 访问相邻节点
            dfs(grid, i - 1, j)
            dfs(grid, i + 1, j)
            dfs(grid, i, j + 1)
            dfs(grid, i, j - 1)

        count = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == "1":
                    dfs(grid, i, j) # 传入参数必须指示我们现在需要搜索的格子在哪里
                    count += 1
        return count
5.腐烂的橘子
class Solution(object):
    def orangesRotting(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        # 思路:
        # 这里的腐烂橘子 相当于层序遍历网格,如果层序遍历结束后任然存在未腐烂 则是-1
        m, n = len(grid), len(grid[0])
        fresh = 0
        q = []
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == 1:
                    fresh += 1
                if grid[i][j] == 2:
                    q.append((i, j)) # 把一开始就腐烂的橘子放进去
        
        ans = -1
        while q:
            ans += 1 # 每一层就加1 代表经过1分钟
            tmp = q
            q = []
            for x,y in tmp: # 取出已经腐烂的橘子开始腐烂其他
                for i, j in (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1):
                    if 0 <= i < m and 0 <= j < n and grid[i][j] == 1:
                        fresh -= 1 # 新鲜橘子数-1
                        grid[i][j] = 2
                        q.append((i, j)) # 把该层访问的橘子都放进去
        # max(ans, 0) 避免初始情况没有新鲜橘子和腐烂橘子的情况,这个时候fresh=0,ans = -1
        return -1 if fresh else max(ans, 0)
6.课程表
  • 代码链接
  • 拓扑排序
  • 思路是通过 拓扑排序 判断此课程安排图是否是 有向无环图(DAG) 。 拓扑排序原理: 对 DAG 的顶点进行排序,使得对每一条有向边 (u,v),均有 u(在排序记录中)比 v 先出现。亦可理解为对某点 v 而言,只有当 v 的所有源点均出现了,v 才能出现。
class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        # 记录每个课程的入度情况
        inDegree = [0] * numCourses
        course_map = [[] for _ in range(numCourses)]
        #  prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。
        # 那么 ai 的入度+1
        for a,b in prerequisites:
            inDegree[a] += 1
            course_map[b].append(a) # 说明要学习a就必须学b,那么存储下来 b是哪些课程的先修课程
        queue = deque()
        # 找到所有入度为0的所有课程可以先修
        for i in range(len(inDegree)):
            if not inDegree[i]:
                queue.append(i) # 把先修课程都先取出放入队列

        while queue:
            pre = queue.popleft()
            numCourses -= 1
            for cur in course_map[pre]:
                inDegree[cur] -= 1 # 所以pre修完后 对应的 cur课程的入度-1
                if not inDegree[cur]: # 如果此时课程入度为0 则可以加入 为新的先修课程
                    queue.append(cur)
        return not numCourses # 如果numCourses=0 说明修完了,否则未修完课程
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值