数据结构 - 二叉树
二叉樹的遍歷思想在很多算法中體現(xiàn)。快速排序的本質(zhì)是二叉樹的先根遍歷,歸并排序和分治算法本質(zhì)是后根遍歷。
"二叉樹題目的難點(diǎn)在于如何通過(guò)題目的要求思考出 每一個(gè)節(jié)點(diǎn)需要做什么,在什么時(shí)候做 "。
文章目錄
- 1.翻轉(zhuǎn)二叉樹
- 2.填充二叉樹節(jié)點(diǎn)的右側(cè)指針*
- 3.將二叉樹按先根順序展開成鏈表(即只有右子節(jié)點(diǎn)的樹)
- 4.根據(jù)數(shù)組構(gòu)建最大二叉樹
- 5.通過(guò)前序和中序遍歷結(jié)果構(gòu)造二叉樹
- 6.尋找相同子樹——序列化*
- 7.BST中第k大的元素
- 8.BST轉(zhuǎn)換為累加樹
- 9.判斷BST是否合法
- 10.刪除BST指定節(jié)點(diǎn)**
- 11.路徑總和III
- 12.合并二叉樹
- 13.BST轉(zhuǎn)雙向有序循環(huán)鏈表*
- 14.樹的子結(jié)構(gòu)
1.翻轉(zhuǎn)二叉樹
寫遞歸函數(shù)時(shí)首先確定遞歸出口。
本題可以使用先根遍歷,也可以使用后根遍歷。
每個(gè)節(jié)點(diǎn)應(yīng)該讓自己的左右子樹翻轉(zhuǎn)后,交換其位置。也可以先交換再翻轉(zhuǎn)。
2.填充二叉樹節(jié)點(diǎn)的右側(cè)指針*
leetcode116題。拉不拉東的經(jīng)典遞歸法:
每個(gè)節(jié)點(diǎn)應(yīng)該把左子節(jié)點(diǎn)指向右子節(jié)點(diǎn),并且當(dāng)該節(jié)點(diǎn)next不為空時(shí),其右子節(jié)點(diǎn)指向next的左子節(jié)點(diǎn)。
另一種巧妙的思路,避免了新建方法。利用先根遍歷的特性,從上往下進(jìn)行連接。當(dāng)前節(jié)點(diǎn)的next不為None時(shí),必有右兄弟,需要將右兄弟的左子節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)的右子節(jié)點(diǎn)連接。
""" # 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 root# 連接相同父親的節(jié)點(diǎn)root.left.next = root.rightif root.next is not None:root.right.next = root.next.leftself.connect(root.left)self.connect(root.right)return root3.將二叉樹按先根順序展開成鏈表(即只有右子節(jié)點(diǎn)的樹)
題目要求:
每個(gè)節(jié)點(diǎn)應(yīng)該把自己的左右子樹拉直,并把拉直的左子樹放到原來(lái)右子樹的位置,把拉直的右子樹接在拉直的左子樹下面。
4.根據(jù)數(shù)組構(gòu)建最大二叉樹
- 最大二叉樹:左子樹是由數(shù)組中,最大元素左邊的數(shù)字構(gòu)成的最大二叉樹。
- 先根遍歷,思想類似于快速排序。
還是要明白每個(gè)節(jié)點(diǎn)該干什么,什么時(shí)候干。
每個(gè)節(jié)點(diǎn)應(yīng)該找到數(shù)組的最大元素,將左右數(shù)組分別送給左右子樹。
5.通過(guò)前序和中序遍歷結(jié)果構(gòu)造二叉樹
- 前序的第一個(gè)元素為整棵樹的根節(jié)點(diǎn),中序遍歷中,根節(jié)點(diǎn)左側(cè)元素構(gòu)成左子樹,右側(cè)元素構(gòu)成右子樹,遞歸構(gòu)造即可。
知道這個(gè)規(guī)律后很簡(jiǎn)單,和4幾乎完全一致。
6.尋找相同子樹——序列化*
- 大致思路是,采用后根遍歷,自底向上進(jìn)行(因?yàn)橐戎赖讓拥慕Y(jié)構(gòu),才能判斷有無(wú)相同子樹)。
- 既然比較“相同”,需要設(shè)置備忘錄,記錄已經(jīng)見過(guò)的樹的結(jié)構(gòu)。
- 最后通過(guò)的做法,引入序列化,字符串比較替換遞歸的節(jié)點(diǎn)比較。
7.BST中第k大的元素
凡提及BST,一定用到的思路是:
BST左子樹的節(jié)點(diǎn)都小于當(dāng)前節(jié)點(diǎn),右子樹節(jié)點(diǎn)都大于當(dāng)前節(jié)點(diǎn)。
BST的中序遍歷是升序序列。
8.BST轉(zhuǎn)換為累加樹
- 題目要求:給出BST的根節(jié)點(diǎn),該樹的節(jié)點(diǎn)值各不相同,請(qǐng)你將其轉(zhuǎn)換為累加樹,使每個(gè)節(jié)點(diǎn) node 的新值等于原樹中大于或等于 node.val 的值之和。
- 從題目得知,累加操作要從最大的元素開始,直到最小的元素。
- 很快想到要用 右子樹->根節(jié)點(diǎn)->左子樹 的中根遍歷順序解決問(wèn)題。
9.判斷BST是否合法
最簡(jiǎn)單的思路是中序遍歷檢查是否遞增,只需記錄前一個(gè)節(jié)點(diǎn)的值
需要自底向上進(jìn)行,后根遍歷。
對(duì)于每個(gè)節(jié)點(diǎn),保證自己的左右子樹為BST,同時(shí)節(jié)點(diǎn)大于左子樹最大值,小于右子樹最小值。
10.刪除BST指定節(jié)點(diǎn)**
對(duì)于不具有子節(jié)點(diǎn)的待刪除點(diǎn),直接刪除即可。
具有單個(gè)子節(jié)點(diǎn),用子節(jié)點(diǎn)替換。
具有兩個(gè)子節(jié)點(diǎn),需要用左子樹的最大節(jié)點(diǎn)/右子樹的最小節(jié)點(diǎn)替換。
利用節(jié)點(diǎn)作為返回值,減少冗余代碼。
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object):def deleteNode(self, root, key):""":type root: TreeNode:type key: int:rtype: TreeNode"""# 不存在待刪除節(jié)點(diǎn)if root is None:return None# 先根遍歷# 分情況討論:待刪節(jié)點(diǎn)無(wú)子節(jié)點(diǎn)、有一個(gè)子節(jié)點(diǎn)、有兩個(gè)子節(jié)點(diǎn)elif 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.leftelif root.left is None and root.right is not None:return root.rightelse:# 有兩個(gè)子節(jié)點(diǎn)時(shí),讓左側(cè)最大或右側(cè)最小的節(jié)點(diǎn)替代該節(jié)點(diǎn)# 這里選擇用左側(cè)最大的節(jié)點(diǎn)left_node = root.leftleft_node_father = rootwhile (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) # ***精髓***# 當(dāng)前節(jié)點(diǎn)不是待刪節(jié)點(diǎn),交給子樹處理elif root.val > key:root.left = self.deleteNode(root.left, key)else:root.right = self.deleteNode(root.right, key)return root11.路徑總和III
給定一個(gè)二叉樹的根節(jié)點(diǎn) root ,和一個(gè)整數(shù) targetSum ,求該二叉樹里節(jié)點(diǎn)值之和等于 targetSum 的 路徑 的數(shù)目。
路徑 不需要從根節(jié)點(diǎn)開始,也不需要在葉子節(jié)點(diǎn)結(jié)束,但是路徑方向必須是向下的(只能從父節(jié)點(diǎn)到子節(jié)點(diǎn))。
- 使用DFS+前綴和的思路。DFS的一個(gè)明顯好處是,每條正在搜索的路徑一定符合題目中對(duì)路徑的要求
- 將 path[0]設(shè)置為0,可以方便統(tǒng)計(jì)從根節(jié)點(diǎn)到某個(gè)節(jié)點(diǎn)的前綴和
12.合并二叉樹
合并的規(guī)則是如果兩個(gè)節(jié)點(diǎn)重疊,那么將他們的值相加作為節(jié)點(diǎn)合并后的新值,否則不為 NULL 的節(jié)點(diǎn)將直接作為新二叉樹的節(jié)點(diǎn)。
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 root1elif root1 is None and root2 is not None:return root2else:return TreeNode(root1.val + root2.val, self.mergeTrees(root1.left, root2.left), self.mergeTrees(root1.right, root2.right))13.BST轉(zhuǎn)雙向有序循環(huán)鏈表*
- 利用中序遍歷BST得到遞增序列的規(guī)則,使用全局的遍歷 head 記錄鏈表頭,pre 記錄之前的節(jié)點(diǎn)
- 一個(gè)比較巧妙的思想:遞歸結(jié)束時(shí) pre 是最后的節(jié)點(diǎn),和 head 首尾相連
14.樹的子結(jié)構(gòu)
- 判斷判斷B是不是A的子結(jié)構(gòu)
- 最后一行是核心:分別判斷 pRoot2 是否是以 pRoot1、pRoot1.left、pRoot1.right 為根的樹的子結(jié)構(gòu)
HasSubTree(a, b):a中是否包含b,a不一定是首節(jié)點(diǎn)
find(a, b):從節(jié)點(diǎn)a和b開始,a是否包含b
總結(jié)
以上是生活随笔為你收集整理的数据结构 - 二叉树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【IT之家开箱】OPPO Reno11
- 下一篇: 算法 - DFS/BFS