LeetCode113.Path Sum II Python实现(详细解析以及错误展示分析)

LeetCode112.Path Sum
LeetCode437.Path Sum III
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.

Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,

      5
     / \
    4   8
   /   / \
  11  13  4
 /  \    / \
7    2  5   1
Return:
[
   [5,4,11,2],
   [5,8,4,5]
]
分析

LeetCode112.Path Sum题一样,只不过这一题需要返回的是具体的路径列表。首先建立两个列表,一个作为返回结果列表Path(大P),另一个中间列表path(小p)。

我们很自然会想到在遍历到某一个点的时候需要将其root.val的值加进中间列表,已经在满足条件(即sum值和叶节点两个条件同时满足)时候将当前的path列表添加到返回结果的Path里面。
在这里结合自己的做题心路整理几点注意地方:

  1. 当遍历到一个不满足条件的叶节点后,需要回溯返回上一个父节点,同时把当前的节点值弹出,也就是递归回溯和pop()操作
  2. pop操作需要在if条件判断之外,也就是说当有一个满足情况的叶节点被遍历后,在返回之后仍然需要弹出它的值,以便遍历它的兄弟节点或者结束递归。
  3. 在有满足条件的时候,将当前路径path添加到返回结果Path之中时候,不可直接Path.append(path)。
    关于第2和3点的情况我会在下面通过实验情况展示。
Code
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def __init__(self):
        self.Path = []
        
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        if root:
            path = []
            self.helper(path,root,sum)
        return self.Path
      
    def helper(self,path,root,sum):
        if root:
            path.append(root.val)
            if root.val == sum and self.isLeaf(root):
                self.Path.append(path)
            
            self.helper(path, root.left, sum-root.val)
            self.helper(path, root.right, sum-root.val)
            path.pop()
    
    def isLeaf(self, root):
        if root:
            if root.left is None and root.right is None:
                return True

为了能够清楚地展示出path和Path的变化过程,在其变化前后分别加上print函数。

    def helper(self, path, root, sum):
        if root:
            path.append(root.val)
            if root.val == sum and self.isLeaf(root):
                self.Path.append(path.copy())
            print('path = ', path)
            print('Path = ', self.Path)
            self.helper(path, root.left, sum - root.val)
            self.helper(path, root.right, sum - root.val)
            path.pop()
            print('path = ', path)

运行结果如下:
在这里插入图片描述

通过上图我们就可以清楚的看出来path和Path变化过程的

错误情况展示
关于第2点的情况,也就是pop在if…else…循环内的情况
    def helper(self,path,root,sum):
        if root:
            path.append(root.val)
            if root.val == sum and self.isLeaf(root):
                self.Path.append(path.copy())
            else:
                self.helper(path, root.left, sum-root.val)
                self.helper(path, root.right, sum-root.val)
                path.pop()

注意,这里的pop和递归是在else之内的。首先,递归在不在else内无所谓,因为只要不是满足条件的叶节点都会执行else的内容,满足情况的叶节点执行else的内容也是返回None,所以关于两个递归语句并不影响。而对于pop而言,放在else里面,则会在出现满足情况的叶节点时候不再执行else语句,这样就会少一次pop,因此在遍历兄弟节点或者返回上一级的时候,就会多出来一个数。每有一个这样的情况,就会少pop一次,从而导致最终结果的错误。
在这里插入图片描述
以这个例子来看,在第二个正确路径被找到前,if语句执行了一次,也就是会少pop一次,因此左边部分的4未能从path中pop出来就被调用到右边部分的递归中去,最终导致8前面多出来一个4。

关于第3点的情况,也就是不用path.copy()而是直接用path对Path进行添加。
def helper(self,path,root,sum):
        if root:
            path.append(root.val)
            if root.val == sum and self.isLeaf(root):
                self.Path.append(path)
            else:
                self.helper(path, root.left, sum-root.val)
                self.helper(path, root.right, sum-root.val)
            path.pop()

注意,上面的helper函数只是和正确的函数在self.Path.append(path)有区别。
在这里插入图片描述

这里如果不加path.copy()的时候就会导致,添加进Path结果中的path和外面继续执行的path仍然是一个对象,也就是说是会继续改变的。这样就会在最后导致所有添加进结果Path列表的path都是一样的,而且在最后的递归全部都结束后中间列表path里面的所有值都被弹出,path和返回结果Path都将是空列表。(具体变化过程可以参见上面的path和Path变化过程图,最后的path是变为空列表的[ ]
而如果用了copy函数后,就相当于复制创建了一个新的对象,和原来的path的存储地址也是不同的,所以不会继续发生改变。

为了更加直观展示,这里附上我在PyCharm里面对这个的单独测试。

list1 = ['Google', 'Runoob', 'Taobao', 'Baidu']
list1.append('Leetcode')
print(id(list1))
print(id(list1.copy()))
print(list1)
L = []
L.append(list1)
print(L)
L1 = []
L1.append(list1.copy())
print(L1)
list1.append('Alibaba')
print(L)
print(L1)

返回结果为:

296565896
296566024
['Google', 'Runoob', 'Taobao', 'Baidu', 'Leetcode']
[['Google', 'Runoob', 'Taobao', 'Baidu', 'Leetcode']]
[['Google', 'Runoob', 'Taobao', 'Baidu', 'Leetcode']]
[['Google', 'Runoob', 'Taobao', 'Baidu', 'Leetcode', 'Alibaba']]
[['Google', 'Runoob', 'Taobao', 'Baidu', 'Leetcode']]

我们可以看到list1和list1.copy()的id地址是不一样的,也即是二者不是一个对象。在通过直接append将list1或list.copy将二者分别加入到L和L1此时发现二者并无区别和list1是一样的。但是当我们继续对list1进行修改后发现,L仍然会发生变化,而L1不再变化。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于LeetCode上的问题994.腐烂的橘子,你可以使用Python来解决。下面是一个示例代码: ```python from collections import deque def orangesRotting(grid): # 记录网格的行数和列数 row, col = len(grid), len(grid[0]) # 定义四个方向:上、下、左、右 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 使用队列来保存腐烂的橘子的位置 queue = deque() # 记录新鲜橘子的数量 fresh_count = 0 # 遍历整个网格,初始化队列和新鲜橘子的数量 for i in range(row): for j in range(col): if grid[i][j] == 2: # 腐烂的橘子 queue.append((i, j)) elif grid[i][j] == 1: # 新鲜橘子 fresh_count += 1 # 如果新鲜橘子的数量为0,直接返回0 if fresh_count == 0: return 0 # 初始化分钟数 minutes = 0 # 开始进行BFS,直到队列为空 while queue: # 记录当前分钟数下,队列中的元素数量 size = len(queue) # 遍历当前分钟数下的所有腐烂的橘子 for _ in range(size): x, y = queue.popleft() # 遍历四个方向 for dx, dy in directions: nx, ny = x + dx, y + dy # 判断新位置是否在网格内,并且是新鲜橘子 if 0 <= nx < row and 0 <= ny < col and grid[nx][ny] == 1: # 将新鲜橘子变为腐烂状态 grid[nx][ny] = 2 # 将新鲜橘子的位置加入队列 queue.append((nx, ny)) # 新鲜橘子的数量减1 fresh_count -= 1 # 如果当前分钟数下,没有新鲜橘子了,结束循环 if fresh_count == 0: break # 每遍历完一层,分钟数加1 minutes += 1 # 如果最后还有新鲜橘子,返回-1,否则返回分钟数 return -1 if fresh_count > 0 else minutes ``` 你可以将给定的网格作为参数传递给`orangesRotting`函数来测试它。请注意,该代码使用了BFS算法来遍历橘子,并计算腐烂的分钟数。希望能对你有所帮助!如果有任何疑问,请随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值