題目介紹
描述:
給你一棵二叉樹,請你返回滿足以下條件的所有節點的值之和:
該節點的祖父節點的值爲偶數。(一個節點的祖父節點是指該節點的父節點的父節點。) 如果不存在祖父節點值爲偶數的節點,那麼返回 0 。
輸入:root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
輸出:18
解釋:圖中紅色節點的祖父節點的值爲偶數,藍色節點爲這些紅色節點的祖父節點。
解題思路:
遞歸算法的關鍵是要明確函數的「定義」是什麼,然後相信這個定義,利用這個定義推導最終結果。
寫樹相關的算法,簡單說就是,先搞清楚當前 root 節點該做什麼,然後根據函數定義遞歸調用子節點,遞歸調用會讓孩子節點做相同的事情。
二叉樹題目的一個難點在於如何通過題目的要求思考出每一個節點需要做什麼
二叉樹解題策略
一 遞歸 二 隊列 + 迭代 (層次遍歷) 三 棧 + 迭代 (非遞歸遍歷) 四 其它
三種基本的遍歷方式,都可以用遞歸來實現。寫遞歸算法的時候,需要注意遞歸退出條件以及遞歸操作的表達。
自己的解法實現
def sumEvenGrandparent4(self, root):
if not root and (not root.left or root.right):
return 0
stack, res = [root], 0
while stack:
node = stack.pop()
if node.val % 2 == 0:
if node.left:
if node.left.left:
res += node.left.left.val
if node.left.right:
res += node.left.right.val
if node.right:
if node.right.right:
res += node.right.right.val
if node.right.left:
res += node.right.left.val
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return res
網上比較優秀的解法
解法一
方法一:深度優先搜索 我們可以通過深度優先搜索找出所有滿足題目要求的節點。
具體地,在進行搜索時,搜索狀態除了當前節點之外,還需要存儲該節點的祖父節點和父節點,即三元組 (grandparent, parent, node)。如果節點 grandparent 的值爲偶數,那麼就將節點 node 的值加入答案。在這之後,我們繼續搜索節點 node 的左孩子 (parent, node, node.left) 以及右孩子 (parent, node, node.right),直到搜索結束。
def sumEvenGrandparent(self, root):
if not root and (not root.left or root.right):
return 0
res = 0
def dfs(grandparent, parent, node):
if not node: return
if grandparent.val % 2 == 0:
nonlocal res
res += node.val
dfs(parent, node, node.left)
dfs(parent, node, node.right)
if root.left:
dfs(root, root.left, root.left.left)
dfs(root, root.left, root.left.right)
if root.right:
dfs(root, root.right, root.right.right)
dfs(root, root.right, root.right.left)
return res
解法二
根節點沒有父節點,根節點的子節點沒有祖父節點,那麼搜索狀態中的grandparent 和 parent 無法進行表示,因此我們必須從根節點的孫子節點(即子節點的子節點)開始搜索。而我們發現,在搜索狀態三元組 (grandparent, parent, node) 中,grandparent 和 parent 這兩項我們只使用了它的值,而不使用節點本身,因此我們可以在搜索狀態中用值來替換這些節點。
def sumEvenGrandparent2(self, root):
res = 0
def dfs(gp_val, p_val, node):
if not node: return
if gp_val % 2 == 0:
nonlocal res
res += node.val
dfs(p_val, node.val, node.left)
dfs(p_val, node.val, node.right)
dfs(1, 1, root)
return res
解法三
方法二:廣度優先搜索 既然要找出祖父節點的值爲偶數的節點,我們不如找到所有值爲偶數的節點,並對這些節點的孫子節點(即子節點的子節點)統計答案。
這樣我們就可以使用廣度優先搜索遍歷整棵樹,當我們找到一個值爲偶數的節點時,我們將該節點的所有孫子節點的值加入答案。
def sumEvenGrandparent3(self, root):
from collections import deque
queue, res = deque([root]), 0
while queue:
node = queue.popleft()
if node.val %2 == 0:
if node.left:
if node.left.left:
res += node.left.left.val
if node.left.right:
res += node.left.right.val
if node.right:
if node.right.right:
res += node.right.right.val
if node.right.left:
res += node.right.left.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
解法四
直接二叉樹遍歷,但是需要在遞歸過程中傳入父節點和祖父節點的值
def sumEvenGrandparent5(self, root):
self.res = 0
def recursion(node, level2, level1):
if node:
recursion(node.left, level1, node.val)
recursion(node.right, level1, node.val)
if level2 % 2 == 0 and level2 != 0:
self.res += node.val
recursion(root, 0, 0)
return self.res
解法五
nonlocal關鍵字用來在函數或其他作用域中使用外層(非全局)變量。
def sumEvenGrandparent6(self, root):
if not root and (not root.left or root.right):
return 0
res = 0
def dfs(node, cur):
if not node: return
nonlocal res
cur.append(node.val)
if len(cur) >= 3 and cur[-3]%2 == 0: # 用列表記錄當前路徑上的點的值
res += node.val
dfs(node.left, cur[:])
dfs(node.right, cur[:])
dfs(root, [])
return res
相關知識總結和思考
相關知識:
BFS:廣度/寬度優先。其實就是從上到下,先把每一層遍歷完之後再遍歷一下一層。
可以使用Queue的數據結構。我們將root節點初始化進隊列,通過消耗尾部,插入頭部的方式來完成BFS。
二叉搜索樹(BST)的特性:
- 若它的左子樹不爲空,則所有左子樹上的值均小於其根節點的值
- 若它的右子樹不爲空,則所有右子樹上的值均大於其根節點的值
- 它的左右子樹也分別爲二叉搜索樹
遞歸與迭代的區別
遞歸:重複調用函數自身實現循環稱爲遞歸; 迭代:利用變量的原值推出新值稱爲迭代,或者說迭代是函數內某段代碼實現循環;