参考代码随想录
终于进入二叉树专题,很兴奋也很害怕
树(Tree):由 n > 0 个节点与节点组合组成的有限集合。当 n = 0 时,树被称为空树;当 n > 0 时,树被称为非空树。
0
。0
。二叉树(Binary Tree):树中各个节点度不大于2的有序树称为二叉树。
满二叉树(Full Binary Tree):如果所有分支节点都存在左子树和右子树,并且所有叶子节点都在同一层上,则称该二叉树为满二叉树。
完全二叉树(Complete Binary Tree):如果叶子节点只能出现在最下面两层,并且最下层的叶子节点都依次排列在该层最左边的位置上,具有这种特点的二叉树称为完全二叉树。
完全二叉树要满足:
h
层(h
从1
开始),则最底层节点个数为1 ~ 2^(h-1)
注意:满二叉树是完全二叉树的一种特殊情况。
二叉搜索树是一个有序树,并满足一下性质:
平衡二叉搜索树(Balanced Binary Tree):又称为AVL Tree。是一种结构平衡的二叉搜索树。即叶节点高度差的绝对值不超过1,并且左、右两个子树都是一棵平衡二叉搜索树。
AVL树满足以下性质:
T
是一个AVL树,它的左、右子树也是AVL树O(logn)
二叉树的存储结构分为「顺序存储」和「链式存储」。
数组和二叉树对应关系:
i
,且该节点不是叶子节点,其左子节点下标为2 * i + 1
,右子节点下标为2 * i + 2
。i
,且该节点是叶子节点,其根节点下标为(i - 1) // 2
顺序存储特点:
node
包含一个数据域val
存储节点信息,包含两个指针域left
和right
,分别指向左子节点和右子节点。二叉树链式存储的定义:
Python
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Java
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
// constructor
TreeNode() {}
TreeNode(int val) {this.val = val;}
TreeNode(int val, TreeNode left, TreeNode left) {
this.val = val;
this.left = left;
this.right = right;
}
}
二叉树遍历主要有两种方式:「深度优先遍历」和「广度优先遍历」
从二叉树的根节点root
出发,先遍历完根节点的左子树所有节点,然后回到根节点继续遍历根节点的右子树所有节点。整个过程反复进行直到所有节点都被访问完。
深度优先遍历(DFS)可以分为「前序遍历」(Preorder)、「中序遍历」(Inorder)和「后序遍历」(Postorder)。
例如:
前序遍历:5 4 1 2 6 7 8
中序遍历:1 4 2 5 7 6 8
后序遍历:1 2 4 7 8 6 5
前续遍历规则:
所以前序遍历顺序为:中 -> 左 -> 右
递归写法的三要素:
- 确定递归的 参数和返回值
- 确定递归的 终止条件
- 确定 单层递归的逻辑
前序遍历的递归实现:
# LC144-二叉树的前序遍历
def preorder(cur: Optional[TreeNode], res: List[int]) -> None:
if cur == None: return
res.append(cur.val) # 中
preorderTraversal(cur.left, res) # 左
preorderTraversal(cur.right, res) # 右
完整代码:
# 前序遍历-递归-LC144_二叉树的前序遍历
# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
# 前序遍历的递归实现
res = list()
def preorder(cur: TreeNode, vec: List[int]) -> None:
# 终止条件
if not cur: return
vec.append(cur.val) # 中
preorder(cur.left, vec) # 左
preorder(cur.right, vec) # 右
preorder(root, res)
return res
// 前序遍历-递归-LC144_二叉树的前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List preorderTraversal(TreeNode root) {
// 前序遍历的递归实现
List result = new LinkedList<>();
preorder(root, result);
return result;
}
public void preorder(TreeNode cur, List temp) {
// 终止条件
if (cur == null) {
return;
}
temp.add(cur.val); // 中
preorder(cur.left, temp); // 左
preorder(cur.right, temp); // 右
}
}
迭代实现的基本思路:
- 使用递归就是每一次递归调用都把函数的局部变量、参数、返回值压入栈中,然后等递归返回的时候,从栈顶弹出上一层的各项参数,返回上一层。
- 所以迭代实现递归算法时需要用栈。
前序遍历的迭代实现:
每次遍历时,处理完中间节点后,先把 右节点 压入栈中,然后再把 左节点 压入栈中,这样出栈的时候是 中左右 的顺序。
注意:中节点不入栈
具体算法:
res
stack
,把根节点root
压入栈中stack
不为空时:
node
,并处理该元素,即res
记录该节点的值node
右节点不为空,访问右节点并入栈node
左节点不为空,访问左节点并入栈# 前序遍历-递归-LC144_二叉树的前序遍历
# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
# 前序遍历的迭代实现
# 返回数组
res = []
# 空树
if not root:
return res
# list as stack
# root只在第一次入栈,之后迭代过程中,root不入栈
stack = [root]
# 终止条件为stack为空,说明遍历完树
while stack: # 相当于 len(stack) != 0
node = stack.pop() # 弹出根节点
res.append(node.val) # 访问根节点
if node.right:
stack.append(node.right) # 右子树先入栈
if node.left:
stack.append(node.left) # 左子树后入栈
return res
// 前序遍历-递归-LC144_二叉树的前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List preorderTraversal(TreeNode root) {
// 前序遍历的迭代实现
List result = new LinkedList<>();
// 空树
if (root == null) {
return result;
}
Deque stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
// 先右入栈
if (node.right != null) {
stack.push(node.right);
}
// 后左入栈
if (node.left != null) {
stack.push(node.left);
}
}
return result;
}
}
中续遍历规则:
所以中序遍历顺序为:左 -> 中 -> 右
中序遍历递归的基本思路与前序遍历递归一致。
中序遍历的递归实现:
# LC094-二叉树的中序遍历
def inorder(cur: Optional[TreeNode], res: List[int]) -> None:
if cur == None: return
inorderTraversal(cur.left, res) # 左
res.append(cur.val) # 中
inorderTraversal(cur.right, res) # 右
完整代码:
# 中序遍历-递归-LC094_二叉树的中序遍历
# 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 inorderTraversal(self, root: TreeNode) -> List[int]:
# 前序遍历的递归实现
res = list()
def inorder(cur: TreeNode, vec: List[int]) -> None:
# 终止条件
if not cur: return
inorder(cur.left, vec) # 左
vec.append(cur.val) # 中
inorder(cur.right, vec) # 右
inorder(root, res)
return res
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List inorderTraversal(TreeNode root) {
List res = new ArrayList<>();
inorder(root, res);
return res;
}
public void inorder(TreeNode cur, List temp) {
// 终止条件
if (cur == null) {
return;
}
inorder(cur.left, temp); // 左
temp.add(cur.val); // 中
inorder(cur.right, temp); // 右
}
}
root
开始,不断向下遍历,将遍历到的元素放入栈stack
中stack
中弹出栈顶节点,记录栈顶节点的值root
开始,一层层往下遍历直到树的最底部,然后才开始处理节点;所以在中序遍历中,需要保证在左子树访问完之前,当前的元素不能出栈具体算法:
stack
,初始化返回数组res
stack
弹出栈顶元素node
,并记录该元素的值;然后将访问当前栈顶元素的右子树cur
不为空或者栈stack
不为空,即while cur or stack
cur
不为空,但是栈stack
为空,说明当前节点cur
是一个当前子树的根节点,还需要继续向下访问(继续向下遍历)cur
为空,但是栈stack
不为空,说明已经遍历到当前子树的最底层,需要开始处理节点(即栈弹出栈顶元素并记录)cur
为空和栈stack
也为空时,说明整棵树都遍历到,遍历结束# 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 inorderTraversal(self, root: TreeNode) -> List[int]:
res = list()
# 空树
if not root:
return res
# list as stack
stack = list()
cur = root
while cur or stack:
if cur:
# 从根节点依次向下访问
stack.append(cur)
cur = cur.left
else:
# not cur: 说明遍历到子树的最左节点
# 处理当前节点并访问当前节点的右子树
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List inorderTraversal(TreeNode root) {
List res = new ArrayList<>();
// 空树
if (root == null) {
return res;
}
// deque as stack
Deque stack = new ArrayDeque<>();
TreeNode cur = root;
// 当前节点不为空或栈不为空
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
// 继续向下访问
stack.push(cur);
cur = cur.left;
} else {
// 处理栈顶节点,并访问栈顶节点右子树
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
}
return res;
}
}
后序遍历规则:
所以后序遍历顺序为:左 -> 右 -> 中
后序遍历递归的基本思路与前序遍历递归、中序遍历递归一致。
后序遍历的递归实现:
# LC145-二叉树的后序遍历
def postorder(cur: Optional[TreeNode], res: List[int]) -> None:
if cur == None: return
postorderTraversal(cur.left, res) # 左
postorderTraversal(cur.right, res) # 右
res.append(cur.val) # 中
完整代码:
# 后序遍历-递归-LC145_二叉树的后序遍历
# 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 postorderTraversal(self, root: TreeNode) -> List[int]:
# 后序遍历的递归实现
res = list()
def postorder(cur: TreeNode, vec: List[int]) -> None:
# 终止条件
if not cur: return
postorder(cur.left, vec) # 左
postorder(cur.right, vec) # 右
vec.append(cur.val) # 中
postorder(root, res)
return res
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
postorder(root, res);
return res;
}
public void postorder(TreeNode cur, List temp) {
// 终止条件
if (cur == null) {
return;
}
postorder(cur.left, temp); // 左
postorder(cur.right, temp); // 右
temp.add(cur.val); // 中
}
}
res
时,反转数组,顺序变为 左右中具体算法:
res
stack
,把根节点root
压入栈中stack
不为空时:
node
,res
记录该节点的值node
左节点不为空,访问左节点并入栈node
右节点不为空,访问右节点并入栈res
的顺序为中右左,最后反转返回数组res
,顺序为左右中# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = list()
# 空树
if not root:
return res
# list as stack
stack = [root]
while stack: # 出栈顺序:中右左
# 中
node = stack.pop()
res.append(node.val)
# 左
if node.left:
stack.append(node.left)
# 右
if node.right:
stack.append(node.right)
# 反转res:左右中
return res[::-1]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
// 空树
if (root == null) {
return res;
}
// deque as stack
Deque stack = new ArrayDeque<>();
stack.push(root);
// 出栈顺序:中右左
while (!stack.isEmpty()) {
// 中
TreeNode node = stack.pop();
res.add(node.val);
// 左
if (node.left != null) {
stack.push(node.left);
}
// 右
if (node.right != null) {
stack.push(node.right);
}
}
// 反转res:左右中
Collections.reverse(res);
return res;
}
}