生活随笔
收集整理的這篇文章主要介紹了
数据结构 - 二叉树
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
二叉樹的遍歷思想在很多算法中體現。快速排序的本質是二叉樹的先根遍歷,歸并排序和分治算法本質是后根遍歷。
"二叉樹題目的難點在于如何通過題目的要求思考出 每一個節點需要做什么,在什么時候做 "。
文章目錄
- 1.翻轉二叉樹
- 2.填充二叉樹節點的右側指針*
- 3.將二叉樹按先根順序展開成鏈表(即只有右子節點的樹)
- 4.根據數組構建最大二叉樹
- 5.通過前序和中序遍歷結果構造二叉樹
- 6.尋找相同子樹——序列化*
- 7.BST中第k大的元素
- 8.BST轉換為累加樹
- 9.判斷BST是否合法
- 10.刪除BST指定節點**
- 11.路徑總和III
- 12.合并二叉樹
- 13.BST轉雙向有序循環鏈表*
- 14.樹的子結構
1.翻轉二叉樹
寫遞歸函數時首先確定遞歸出口。
本題可以使用先根遍歷,也可以使用后根遍歷。
每個節點應該讓自己的左右子樹翻轉后,交換其位置。也可以先交換再翻轉。
class Solution(object):def invertTree(self
, root
):""":type root: TreeNode:rtype: TreeNode"""if root
is None:return Noneleft_node
= self
.invertTree
(root
.left
)right_node
= self
.invertTree
(root
.right
)root
.right
= left_noderoot
.left
= right_node
return root
2.填充二叉樹節點的右側指針*
leetcode116題。拉不拉東的經典遞歸法:
每個節點應該把左子節點指向右子節點,并且當該節點next不為空時,其右子節點指向next的左子節點。
"""
# Definition for a Node.
class Node(object):def __init__(self, val=0, left=None, right=None, next=None):self.val = valself.left = leftself.right = rightself.next = next
"""class Solution(object):def connect(self
, root
):""":type root: Node:rtype: Node"""if root
is None:return Noneself
.connectTowNodes
(root
.left
, root
.right
)return root
def connectTowNodes(self
, node1
, node2
):if node1
is None:returnnode1
.next = node2self
.connectTowNodes
(node1
.left
, node1
.right
)self
.connectTowNodes
(node2
.left
, node2
.right
)self
.connectTowNodes
(node1
.right
, node2
.left
)
另一種巧妙的思路,避免了新建方法。利用先根遍歷的特性,從上往下進行連接。當前節點的next不為None時,必有右兄弟,需要將右兄弟的左子節點和當前節點的右子節點連接。
"""
# Definition for a Node.
class Node(object):def __init__(self, val=0, left=None, right=None, next=None):self.val = valself.left = leftself.right = rightself.next = next
"""class Solution(object):def connect(self
, root
):""":type root: Node:rtype: Node"""if root
is None or root
.left
is None:return rootroot
.left
.next = root
.right
if root
.next is not None:root
.right
.next = root
.next.leftself
.connect
(root
.left
)self
.connect
(root
.right
)return root
3.將二叉樹按先根順序展開成鏈表(即只有右子節點的樹)
題目要求:
每個節點應該把自己的左右子樹拉直,并把拉直的左子樹放到原來右子樹的位置,把拉直的右子樹接在拉直的左子樹下面。
class Solution(object):def flatten(self
, root
):""":type root: TreeNode:rtype: None Do not return anything, modify root in-place instead."""if root
is None:return Noneleft_flatten
= self
.flatten
(root
.left
)right_flatten
= self
.flatten
(root
.right
)root
.right
= left_flattenroot
.left
= Noneif (left_flatten
is None):root
.right
= right_flatten
else:while (left_flatten
.right
is not None):left_flatten
= left_flatten
.rightleft_flatten
.right
= right_flatten
return root
4.根據數組構建最大二叉樹
- 最大二叉樹:左子樹是由數組中,最大元素左邊的數字構成的最大二叉樹。
- 先根遍歷,思想類似于快速排序。
還是要明白每個節點該干什么,什么時候干。
每個節點應該找到數組的最大元素,將左右數組分別送給左右子樹。
class Solution(object):def constructMaximumBinaryTree(self
, nums
):""":type nums: List[int]:rtype: TreeNode"""if len(nums
) == 0:return Nonemax_idx
= nums
.index
(max(nums
))return TreeNode
(nums
[max_idx
], self
.constructMaximumBinaryTree
(nums
[: max_idx
]), self
.constructMaximumBinaryTree
(nums
[max_idx
+ 1: ]))
5.通過前序和中序遍歷結果構造二叉樹
- 前序的第一個元素為整棵樹的根節點,中序遍歷中,根節點左側元素構成左子樹,右側元素構成右子樹,遞歸構造即可。
知道這個規律后很簡單,和4幾乎完全一致。
class Solution(object):def buildTree(self
, preorder
, inorder
):""":type preorder: List[int]:type inorder: List[int]:rtype: TreeNode"""length
= len(preorder
)if length
== 0:return Noneroot_val
= preorder
[0]root_inoreder_index
= inorder
.index
(root_val
)left_node_num
= root_inoreder_index left_tree
= self
.buildTree
(preorder
[1:left_node_num
+1], inorder
[:root_inoreder_index
])right_tree
= self
.buildTree
(preorder
[1+left_node_num
:], inorder
[root_inoreder_index
+1:])return TreeNode
(val
=root_val
, left
=left_tree
, right
=right_tree
)
6.尋找相同子樹——序列化*
- 大致思路是,采用后根遍歷,自底向上進行(因為要先知道底層的結構,才能判斷有無相同子樹)。
- 既然比較“相同”,需要設置備忘錄,記錄已經見過的樹的結構。
- 最后通過的做法,引入序列化,字符串比較替換遞歸的節點比較。
class Solution(object):def findDuplicateSubtrees(self
, root
):""":type root: TreeNode:rtype: List[TreeNode]"""res
= [] self
.find
(root
, res
)subtrees
= []for i
in range(len(res
)):if res
[i
][1] > 1:subtrees
.append
(res
[i
][2])return subtrees
def find(self
, node
, result
):if node
is None:returnself
.find
(node
.left
, result
)self
.find
(node
.right
, result
)node_sequence
= self
.transferToSequence
(node
)for i
in range(len(result
)):if result
[i
][0] == node_sequence
:old_time
= result
[i
][1]result
[i
] = (node_sequence
, old_time
+1, node
)returnresult
.append
((node_sequence
, 1, node
))def transferToSequence(self
, node
):if node
is None:return "#"return str(node
.val
) + "," + self
.transferToSequence
(node
.left
) + "," + self
.transferToSequence
(node
.right
)
7.BST中第k大的元素
凡提及BST,一定用到的思路是:
BST左子樹的節點都小于當前節點,右子樹節點都大于當前節點。
BST的中序遍歷是升序序列。
class Solution(object):cnt
= 0def kthLargest(self
, root
, k
):""":type root: TreeNode:type k: int:rtype: int"""self
.cnt
= 0def findK(node
, k
):if node
is None:return Noner
= findK
(node
.right
, k
)if r
is not None: return rself
.cnt
+= 1if self
.cnt
== k
: return node
.vall
= findK
(node
.left
, k
) return l
return findK
(root
, k
)
8.BST轉換為累加樹
- 題目要求:給出BST的根節點,該樹的節點值各不相同,請你將其轉換為累加樹,使每個節點 node 的新值等于原樹中大于或等于 node.val 的值之和。
- 從題目得知,累加操作要從最大的元素開始,直到最小的元素。
- 很快想到要用 右子樹->根節點->左子樹 的中根遍歷順序解決問題。
class Solution(object):temp
= 0def convertBST(self
, node
):""":type root: TreeNode:rtype: TreeNode"""if node
is None:return Noneself
.convertBST
(node
.right
)self
.temp
+= node
.valnode
.val
= self
.tempself
.convertBST
(node
.left
)return node
9.判斷BST是否合法
最簡單的思路是中序遍歷檢查是否遞增,只需記錄前一個節點的值
需要自底向上進行,后根遍歷。
對于每個節點,保證自己的左右子樹為BST,同時節點大于左子樹最大值,小于右子樹最小值。
class Solution(object):def isValidBST(self
, root
):""":type root: TreeNode:rtype: bool"""def findLeftMax(node
):if node
is None or node
.left
is None:return Nonenode
= node
.left
while (node
.right
is not None):node
= node
.right
return node
.val
def findRightMin(node
):if node
is None or node
.right
is None:return Nonenode
= node
.right
while (node
.left
is not None):node
= node
.left
return node
.val
if root
is None:return Trueelse:l
= findLeftMax
(root
)r
= findRightMin
(root
)l
= (root
.val
- 1) if l
is None else lr
= (root
.val
+ 1) if r
is None else r
return l
< root
.val
and r
> root
.val
and self
.isValidBST
(root
.left
) and self
.isValidBST
(root
.right
)
10.刪除BST指定節點**
對于不具有子節點的待刪除點,直接刪除即可。
具有單個子節點,用子節點替換。
具有兩個子節點,需要用左子樹的最大節點/右子樹的最小節點替換。
利用節點作為返回值,減少冗余代碼。
class Solution(object):def deleteNode(self
, root
, key
):""":type root: TreeNode:type key: int:rtype: TreeNode"""if root
is None:return Noneelif root
.val
== key
:if root
.left
is None and root
.right
is None:return Noneelif root
.left
is not None and root
.right
is None:return root
.left
elif root
.left
is None and root
.right
is not None:return root
.right
else:left_node
= root
.leftleft_node_father
= root
while (left_node
.right
is not None):left_node_father
= left_nodeleft_node
= left_node
.rightroot
.val
= left_node
.valroot
.left
= self
.deleteNode
(root
.left
, root
.val
) elif root
.val
> key
:root
.left
= self
.deleteNode
(root
.left
, key
)else:root
.right
= self
.deleteNode
(root
.right
, key
)return root
11.路徑總和III
給定一個二叉樹的根節點 root ,和一個整數 targetSum ,求該二叉樹里節點值之和等于 targetSum 的 路徑 的數目。
路徑 不需要從根節點開始,也不需要在葉子節點結束,但是路徑方向必須是向下的(只能從父節點到子節點)。
- 使用DFS+前綴和的思路。DFS的一個明顯好處是,每條正在搜索的路徑一定符合題目中對路徑的要求
- 將 path[0]設置為0,可以方便統計從根節點到某個節點的前綴和
class Solution(object):res
= 0def pathSum(self
, root
, targetSum
):""":type root: TreeNode:type targetSum: int:rtype: int"""self
.res
= 0path
= [0] def dfs(node
, targetSum
):if node
is None:return Falsepath
.append
(node
.val
+ path
[-1])for i
in range(len(path
) - 1):if path
[-1] - path
[i
] == targetSum
:self
.res
+= 1if dfs
(node
.left
, targetSum
):path
.pop
()if dfs
(node
.right
, targetSum
):path
.pop
()return Truedfs
(root
, targetSum
)return self
.res
--------------------------------------這樣也是可以的
-----------------------------------class Solution(object):res
= 0def pathSum(self
, root
, targetSum
):""":type root: TreeNode:type targetSum: int:rtype: int"""self
.res
= 0path
= [0] def dfs(node
, targetSum
):if node
is None:returnpath
.append
(node
.val
+ path
[-1])for i
in range(len(path
) - 1):if path
[-1] - path
[i
] == targetSum
:self
.res
+= 1dfs
(node
.left
, targetSum
)dfs
(node
.right
, targetSum
)path
.pop
()dfs
(root
, targetSum
)return self
.res
12.合并二叉樹
合并的規則是如果兩個節點重疊,那么將他們的值相加作為節點合并后的新值,否則不為 NULL 的節點將直接作為新二叉樹的節點。
class Solution(object):def mergeTrees(self
, root1
, root2
):""":type root1: TreeNode:type root2: TreeNode:rtype: TreeNode"""if root1
is None and root2
is None:return Noneelif root1
is not None and root2
is None:return root1
elif root1
is None and root2
is not None:return root2
else:return TreeNode
(root1
.val
+ root2
.val
, self
.mergeTrees
(root1
.left
, root2
.left
), self
.mergeTrees
(root1
.right
, root2
.right
))
13.BST轉雙向有序循環鏈表*
- 利用中序遍歷BST得到遞增序列的規則,使用全局的遍歷 head 記錄鏈表頭,pre 記錄之前的節點
- 一個比較巧妙的思想:遞歸結束時 pre 是最后的節點,和 head 首尾相連
class Solution(object):head
= Nonepre
= Nonedef treeToDoublyList(self
, root
):""":type root: Node:rtype: Node"""if root
is None:return Noneself
.head
= Noneself
.pre
= Nonedef midorder(node
):if node
is None:return Nonemidorder
(node
.left
)if self
.pre
is None: self
.head
= node
else:self
.pre
.right
= nodenode
.left
= self
.preself
.pre
= nodemidorder
(node
.right
)midorder
(root
)self
.head
.left
= self
.preself
.pre
.right
= self
.head
return self
.head
14.樹的子結構
- 判斷判斷B是不是A的子結構
- 最后一行是核心:分別判斷 pRoot2 是否是以 pRoot1、pRoot1.left、pRoot1.right 為根的樹的子結構
HasSubTree(a, b):a中是否包含b,a不一定是首節點
find(a, b):從節點a和b開始,a是否包含b
class Solution:def HasSubtree(self
, pRoot1
, pRoot2
):def find(n1
, n2
):if not n2
:return True elif not n1
:return Falseelif n1
.val
!= n2
.val
:return Falseelse:return find
(n1
.left
, n2
.left
) and find
(n1
.right
, n2
.right
)if not pRoot1
or not pRoot2
:return Falsereturn find
(pRoot1
, pRoot2
) or self
.HasSubtree
(pRoot1
.left
, pRoot2
) or self
.HasSubtree
(pRoot1
.right
, pRoot2
)
總結
以上是生活随笔為你收集整理的数据结构 - 二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。