本來這只是一個普通的算法題,但是當初自己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的可變對象、不可變對象等機制。下一節,會對該問題進行詳述!