二叉树的遍历方式
二叉树的存储方式: 链式存储(指针)和顺序存储(数组)。
- 数组存储二叉树:
如果父节点的数组下表是i,那么它的左孩子就是i * 2 + 1,右孩子就是 i * 2 + 2。 - 一般用链式存储二叉树:
二叉树的遍历:
- 深度优先遍历:
前序遍历(递归法,迭代法)
中序遍历(递归法,迭代法)
后序遍历(递归法,迭代法) - 广度优先遍历:
层次遍历(迭代法)
前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
二叉树的定义:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
1、二叉树的层序遍历(队列-done)
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
"""二叉树层序遍历迭代解法"""
results = []
if not root:
return results
from collections import deque
que = deque([root]) #队列初始化
while que:
size = len(que) #求队列长度
result = []
for _ in range(size):
cur = que.popleft() #从左边出队
result.append(cur.val)
if cur.left:
que.append(cur.left) #从右边入队
if cur.right:
que.append(cur.right)
results.append(result)
return results
2、二叉树的右视图(基于102,done)
199 二叉树的右视图
层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中
results.append(result[-1])
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
results = []
if not root:
return results
from collections import deque
que = deque([root]) #初始化队列
while que:
#层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中
#每次都取最后一个node就可以了
node = que[-1]
results.append(node.val)
for _ in range(len(que)):
cur = que.popleft()
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return results
3、N 叉树的层序遍历(基于102,done)
429 N 叉树的层序遍历
将左右子树换成children,但que.extend(cur.children) #用extend而非append
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
results = []
if not root:
return results
from collections import deque
que = deque([root])
while que:
result = []
for _ in range(len(que)):
cur = que.popleft()
result.append(cur.val)
# cur.children 是 Node 对象组成的列表,也可能为 None
if cur.children:
que.extend(cur.children) #用extend而非append
results.append(result)
return results
4、填充每个节点的下一个右侧节点指针(……not done)
116 填充每个节点的下一个右侧节点指针
在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点
"""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return None
queue = [root]
while queue:
n = len(queue)
for i in range(n):
node = queue.pop(0)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if i == n - 1:
break
node.next = queue[0]
return root
5、填充每个节点的下一个右侧节点指针II(同4)
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return None
queue = [root]
while queue: # 遍历每一层
length = len(queue)
tail = None # 每一层维护一个尾节点
for i in range(length): # 遍历当前层
curnode = queue.pop(0)
if tail:
tail.next = curnode # 让尾节点指向当前节点
tail = curnode # 让当前节点成为尾节点
if curnode.left : queue.append(curnode.left)
if curnode.right: queue.append(curnode.right)
return root
6、按之字形顺序打印二叉树(need again
)
剑指 Offer 32 - III. 从上到下打印二叉树 III
result = deque()也使用队列
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
res = []
if not root:
return []
from collections import deque
que = deque([root])
while que:
result = deque()
for _ in range(len(que)):
cur = que.popleft()
if len(res) % 2: result.appendleft(cur.val) # 偶数层 -> 队列头部
else: result.append(cur.val) # 奇数层 -> 队列尾部
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(list(result))
return res
7、 找树左下脚的值(层序遍历-done)
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
results = 0
if not root:
return results
from collections import deque
que = deque([root])
while que:
node = que[0]
for _ in range(len(que)):
cur = que.popleft()
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
results = node.val
return results
8、 求根节点到叶节点数字之和
class Solution:
def sumNumbers(self, root: TreeNode) -> int:
def dfs(root: TreeNode, prevTotal: int) -> int:
if not root:
return 0
total = prevTotal * 10 + root.val
if not root.left and not root.right:
return total
else:
return dfs(root.left, total) + dfs(root.right, total)
return dfs(root, 0)
9、 二叉树的完全性检验
class Solution:
def isCompleteTree(self, root: TreeNode) -> bool:
nodes = [(root, 1)]
i = 0
while i < len(nodes):
node, v = nodes[i]
i += 1
if node:
nodes.append((node.left, 2*v))
nodes.append((node.right, 2*v+1))
return nodes[-1][1] == len(nodes)
二叉树的属性
1、二叉树的最大深度(递归-done)
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
return 1+ max(self.maxDepth(root.left),self.maxDepth(root.right))
2、二叉树的最小深度(递归-need again
)
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.left and not root.right:
return 1
Depth = 10**5
if root.left:
Depth = min(self.minDepth(root.left),Depth) # 获得左子树的最小高度
if root.right:
Depth = min(self.minDepth(root.right),Depth) # 获得右子树的最小高度
return Depth + 1
3、对称二叉树(递归-done)
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
def im(left,right):
if not left and not right:
return True
if not left or not right:
return False
return left.val == right.val and im(left.left,right.right) and im(left.right,right.left)
return im(root.left,root.right)
4、完全二叉树的节点个数(递归-done)
class Solution:
def countNodes(self, root: TreeNode) -> int:
if not root:
return 0
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
5、平衡二叉树(递归-done)
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def height(root: TreeNode) -> int:
if not root:
return 0
return max(height(root.left), height(root.right)) + 1
if not root:
return True
if abs(height(root.left) - height(root.right)) > 1:
return False
return self.isBalanced(root.left) and self.isBalanced(root.right)
6、二叉树的所有路径(……need again
)
class Solution:
def binaryTreePaths(self, root: TreeNode) -> List[str]:
path = ''
result = []
if not root:
return result
return self.travel(root,path,result)
def travel(self,tree: TreeNode,path:str,result:List):
path += str(tree.val)
if tree.left is None and tree.right is None:
result.append(path)
if tree.left:
self.travel(tree.left,path + "->",result)
if tree.right:
self.travel(tree.right,path + "->",result)
return result
7、路径总和(……need again
)
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def isornot(root, targetsum) -> bool:
if (not root.left) and (not root.right) and targetsum == 0:
return True # 遇到叶子节点,并且计数为0
if (not root.left) and (not root.right):
return False # 遇到叶子节点,计数不为0
if root.left:
targetsum -= root.left.val # 左节点
if isornot(root.left, targetsum): return True # 递归,处理左节点
targetsum += root.left.val # 回溯
if root.right:
targetsum -= root.right.val # 右节点
if isornot(root.right, targetsum): return True # 递归,处理右节点
targetsum += root.right.val # 回溯
return False
if not root:
return False # 别忘记处理空treenode
else:
return isornot(root, targetSum - root.val)
8、左叶子之和(递归-need again
)
class Solution:
def sumOfLeftLeaves(self, root: TreeNode) -> int:
if not root:
return 0
sumOfLeft = 0
if root.left and not root.left.left and not root.left.right: #根节点的左叶子
sumOfLeft = root.left.val
left = self.sumOfLeftLeaves(root.left) #左子树的左叶子之和
right = self.sumOfLeftLeaves(root.right) #右子树的左叶子之和
return sumOfLeft + left + right
二叉树的修改与构造
1、翻转二叉树(前序/后序遍历-done)
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
root.left, root.right = root.right, root.left #根
self.invertTree(root.left) #左
self.invertTree(root.right) #右
return root
2、合并二叉树(done)
class Solution:
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
if not root1:return root2 #如果t1为空,合并之后就应该是t2
if not root2:return root1 #如果t2为空,合并之后就应该是t1
root1.val += root2.val #根
root1.left = self.mergeTrees(root1.left,root2.left) #左
root1.right = self.mergeTrees(root1.right,root2.right) #右
return root1
3、从中序与后序遍历序列构造二叉树(……need again
)
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. (递归终止条件)
if not postorder:
return None
# 第二步: 后序遍历的最后一个就是当前的根节点.
root_val = postorder[-1]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
postorder_left = postorder[:len(inorder_left)]
postorder_right = postorder[len(inorder_left): len(postorder) - 1]
# 第六步: 递归
root.left = self.buildTree(inorder_left, postorder_left)
root.right = self.buildTree(inorder_right, postorder_right)
return root
4、最大二叉树(……not done)
5、删除二叉搜索树中的节点(……not done)
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
if not root: return root #第一种情况:没找到删除的节点,遍历到空节点直接返回了
if root.val == key:
if not root.left and not root.right: #第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
del root
return None
if not root.left and root.right: #第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
tmp = root
root = root.right
del tmp
return root
if root.left and not root.right: #第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
tmp = root
root = root.left
del tmp
return root
else: #第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置
v = root.right
while v.left:
v = v.left
v.left = root.left
tmp = root
root = root.right
del tmp
return root
if root.val > key:
root.left = self.deleteNode(root.left, key)
if root.val < key:
root.right = self.deleteNode(root.right, key)
return root
6、二叉搜索树中的插入操作(……not done)
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)
if root.val > val:
# 将val插入至当前root的左子树中合适的位置
# 并更新当前root的左子树为包含目标val的新左子树
root.left = self.insertIntoBST(root.left,val)
if root.val < val:
root.right = self.insertIntoBST(root.right,val)
return root
7、修剪二叉搜索树(……not done)
class Solution:
def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
if not root: return None
if root.val < low:
# 若当前root节点小于左界:只考虑其右子树,用于替代更新后的其本身,抛弃其左子树整体
return self.trimBST(root.right, low, high)
if root.val > high:
# 若当前root节点大于右界:只考虑其左子树,用于替代更新后的其本身,抛弃其右子树整体
return self.trimBST(root.left, low, high)
if root.val >= low and root.val <= high:
root.left = self.trimBST(root.left,low,high)
root.right = self.trimBST(root.right,low,high)
return root
8、将有序数组转换为二叉搜索树(……not done)
9、把二叉搜索树转换为累加树(……not done)
10、 将有序数组转换为二叉搜索树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
# 返回根节点
root = self.traversal(nums, 0, len(nums)-1)
return root
def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:
if left > right:
return None
# 确定左右界的中心,防越界
mid = left + (right - left) // 2
# 构建根节点
mid_root = TreeNode(nums[mid])
# 构建以左右界的中心为分割点的左右子树
mid_root.left = self.traversal(nums, left, mid-1)
mid_root.right = self.traversal(nums, mid+1, right)
# 返回由被传入的左右界定义的某子树的根节点
return mid_root
二叉搜索树的属性
1、验证二叉搜索树(中序遍历-done)
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
res = []
def dfs(cur:TreeNode):
if not cur:
return False
#左根右
dfs(cur.left)
res.append(cur.val)
dfs(cur.right)
return res
result = dfs(root)
return sorted(result)==result and len(result) == len(set(result))
2、二叉搜索树中的众数(……hard)
class Solution:
def __init__(self):
self.pre = TreeNode()
self.count = 0
self.max_count = 0
self.result = []
def findMode(self, root: TreeNode) -> List[int]:
if not root: return None
self.search_BST(root)
return self.result
def search_BST(self, cur: TreeNode) -> None:
if not cur: return None
self.search_BST(cur.left) #左
# 第一个节点
if not self.pre:
self.count = 1
# 与前一个节点数值相同
elif self.pre.val == cur.val:
self.count += 1
# 与前一个节点数值不相同
else:
self.count = 1
self.pre = cur
if self.count == self.max_count:
self.result.append(cur.val)
if self.count > self.max_count:
self.max_count = self.count
self.result = [cur.val] # 清空self.result,确保result之前的的元素都失效
self.search_BST(cur.right)
3、二叉搜索树中的搜索(递归-done)
700 二叉搜索树中的搜索
若 root为空则返回空节点;若 val=root.val,则返回 root;
若 val<root.val,递归左子树;若 val>root.val,递归右子树。
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
if not root or root.val == val:
return root
if root.val < val:
return self.searchBST(root.right, val)
if root.val > val:
return self.searchBST(root.left, val)
return None
4、二叉搜索树的最小绝对差(中序遍历-done)
class Solution:
def getMinimumDifference(self, root: TreeNode) -> int:
ans = []
def dfs(cur:TreeNode):
if not cur:
return ans
dfs(cur.left)
ans.append(cur.val)
dfs(cur.right)
return ans
res = dfs(root)
mindiff = res[1] - res[0]
for i in range(len(res)-1):
if res[i+1] - res[i] < mindiff:
mindiff = res[i+1] -res[i]
return mindiff
二叉树公共祖先
1、二叉树的最近公共祖先(…… need again
)
236 二叉树的最近公共祖先
如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。
如果left 和 right都不为空,说明此时root就是最近公共节点。
如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root or root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left,p,q)
right = self.lowestCommonAncestor(root.right,p,q)
if left and right:
return root
return left if left else right
2、 二叉搜索树的最近公共祖先(need again
)
235 二叉搜索树的最近公共祖先
==其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。==普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root.val > p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left, p, q)
if root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right, p, q)
return root
3、路径总和ii(……not done
)
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
ret = list()
path = list()
def dfs(root: TreeNode, targetSum: int):
if not root:
return
path.append(root.val)
targetSum -= root.val
if not root.left and not root.right and targetSum == 0:
ret.append(path[:])
dfs(root.left, targetSum)
dfs(root.right, targetSum)
path.pop()
dfs(root, targetSum)
return ret
剑指 Offer
1、二叉搜索树的第k大节点(done)
class Solution:
def KthNode(self , proot: TreeNode, k: int) -> int:
# write code here
if not proot or k < 1:
return -1
ans = []
def dfs(root):
if not root:return -1
dfs(root.left)
ans.append(root.val)
dfs(root.right)
return ans
ans = dfs(proot)
return ans[k-1] if k <= len(ans) else -1
2、重建二叉树(done)
https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/solution/dai-ma-sui-xiang-lu-dai-ni-xue-tou-er-ch-sl1x/
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
if not preorder:
return None
# 第二步: 前序遍历的第一个就是当前的中间节点.
root_val = preorder[0]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的.
preorder_left = preorder[1:1 + len(inorder_left)]
preorder_right = preorder[1 + len(inorder_left):]
# 第六步: 递归
root.left = self.buildTree(preorder_left, inorder_left)
root.right = self.buildTree(preorder_right, inorder_right)
return root
3、树的子结构(……need again
)
26 树的子结构
recur(A, B) 函数:
终止条件:
当节点 B 为空:说明树 B 已匹配完成(越过叶子节点),因此返回 true ;
当节点 A 为空:说明已经越过树 A 叶子节点,即匹配失败,返回 false ;
当节点 A 和 B 的值不同:说明匹配失败,返回 false ;
返回值:
判断 A 和 B 的左子节点是否相等,即 recur(A.left, B.left) ;
判断 A 和 B 的右子节点是否相等,即 recur(A.right, B.right) ;
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
def recur(A, B):
if not B: return True
if not A or A.val != B.val: return False
return recur(A.left, B.left) and recur(A.right, B.right)
return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))
4、二叉搜索树的后序遍历序列(……need again
)
class Solution:
def verifyPostorder(self, postorder: [int]) -> bool:
stack, root = [], float("+inf")
for i in range(len(postorder) - 1, -1, -1):
if postorder[i] > root: return False
while(stack and postorder[i] < stack[-1]):
root = stack.pop()
stack.append(postorder[i])
return True
5、二叉搜索树与双向链表(……不会)
class Solution:
def Convert(self , pRootOfTree ):
if not pRootOfTree:
return None
ans = []
def dfs(root):
if root:
dfs(root.left)
ans.append(root)
dfs(root.right)
dfs(pRootOfTree)
if len(ans) == 1:
return pRootOfTree
# 构造双向链表
for i in range(len(ans)-1):
ans[i].right = ans[i+1]
ans[i+1].left = ans[i]
return ans[0]
6、路径总和iii(……need again
)
class Solution:
def pathSum(self, root: TreeNode, targetSum: int) -> int:
def rootSum(root, targetSum):
if root is None:
return 0
ret = 0
if root.val == targetSum:
ret += 1
ret += rootSum(root.left, targetSum - root.val)
ret += rootSum(root.right, targetSum - root.val)
return ret
if root is None:
return 0
ret = rootSum(root, targetSum)
ret += self.pathSum(root.left, targetSum)
ret += self.pathSum(root.right, targetSum)
return ret