本来这只是一个普通的算法题,但是当初自己OJ上提交时,总是提交失败,而我自己认定程序逻辑没有任何问题。然后开始就在本机上调试,结果发现这是由于Python的对象机制而引发的。所以先把问题算法题贴出来,然后通过该问题,详述Python的对象机制!
题目描述:
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
这道题本身不难,书上、网上也有很多关于该问题的解释,这里我就直接贴上我的Python代码(该代码有一处很隐晦的bug)。
1 # -*- coding:utf-8 -*-
2 # class TreeNode:
3 # def __init__(self, x):
4 # self.val = x
5 # self.left = None
6 # self.right = None
7 class Solution: 8 # 返回二维列表,内部每个列表表示找到的路径
9 def FindPath(self, root, expectNumber): 10 # write code here
11 if not root: 12 return [] 13
14 ret = [] # 函数返回的二维列表
15 path = [] # 遍历过程中经历的路径,假如符合条件,就加入到ret列表中
16 self.Find(root, target, ret, path) 17 return ret 18
19
20 def Find(self, root, target, ret, path): 21 if not root: 22 return
23
24 path.append(root.val) 25 isLeaf = root.left is None and root.right is None 26 if isLeaf and target == root.val: 27 ret.append(path) # 对于叶子节点,加上符合条件的path.
28
29 if root.left: 30 Find(root.left, target - root.val, ret, path) 31 if root.right: 32 Find(root.left, target - root.val, ret, path) 33
34 path.pop()
算法程序逻辑没有任何问题,却总是通不过OJ。一遍遍审查后,实在找不出哪里的错误,然后尝试调试。
看到了吗?程序运行过程中,已经正确的把路径保存下来了。但是,最终程序返回的结果是包含两个[]的列表。
如此,突然感觉豁然开朗了,于是,把第27行换成 ret.append(path[:])
下面是改正后代码,成功通过OJ系统
# -*- coding:utf-8 -*-
class Solution: # 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber): # write code here
if not root: return [] ret = [] path = [] self.Find(root, expectNumber, ret, path) # print ret
# a = [];self.f(a);print a
return ret def Find(self, root, target, ret, path): if not root: return path.append(root.val) isLeaf = (root.left is None and root.right is None) if isLeaf and target == root.val: ret.append(path[:]) # 这里这一步要千万注意啊,
# 假如是:ret.append(path), 结果是错的。因为Python可变对象都是引用传递啊。
#print "target:", target, "isLeaf:", isLeaf,
#print "val:", root.val, "path:", path, "ret:", ret
#print
if root.left: self.Find(root.left, target - root.val, ret, path) if root.right: self.Find(root.right, target - root.val, ret, path) path.pop()
至于为什么这小小的差别,会导致程序完全不同的运行行为,则涉及到Python的可变对象、不可变对象等机制。下一节,会对该问题进行详述!