树的特征
在区分各类树结构之前,先了解一些树的基本特征:
节点的高度 = 节点到叶子节点的最长路径(边数)
节点的深度 = 根节点到这个节点所经历的边个数
节点的层数 = 节点的深度+1
树的高度 = 根节点的高度
图例:
树结构简介
满二叉树、完全二叉树
- 二叉树
二叉树的每个节点最多有两个子节点,且可以为空 - 满二叉树
在一棵二叉树中,如果所有分支节点都存在左右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
性质:
- 层数为k且含有2k-1个节点
- 完全二叉树
对一颗具有n个节点的二叉树按层序号编号,如果编号为i(1<=i<=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
性质:
- 具有n个结点的完全二叉树的深度为[log2n]+1(注:[ ]表示向下取整)
- 如果对一棵有n个节点的完全二叉树的节点按层序编号,则对任一节点i(1<=i<=n) ,有:
- 如果i = 1,则节点i是二叉树的根,无双亲;如果i > 1,则其双亲parent(i)是节点[i/2];
- 如果2i>n,则节点i无左孩子,否则其左孩子是节点2i;
- 如果2i+1>n,则节点i无右孩子,否则其右孩子是节点2i+1。
二叉搜索树
二叉搜索树(Binary Search Tree,BST)又叫二叉查找树
性质:左节点值 < 根节点值 < 右节点值
平衡二叉树
平衡二叉树(Balanced Binary Tree)又被称为AVL树
性质:它是一棵空树或者它的左右两个子树的高度差的绝对值不超过1
深度优先遍历DFS
- 遍历规则
不断沿着定点的深度方向遍历 - 细分
1)先序遍历:根节点——左节点——右节点
2)中序遍历:左节点——根节点——右节点
3)后序遍历:左节点——右节点——根节点 - 分析
不全部保留节点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢。 - Java代码
//深度优先遍历(双端队列)
public void depthFirstSearch(TreeNode root) {
if(root==null) {
return;
}
Deque<TreeNode> deque=new Deque<>();
deque.offerLast(root);
while(!deque.isEmpty()) {
TreeNode node = deque.pollLast(); //弹出栈顶元素
System.out.print(node.data+" ");
if(node.right!=null) {
deque.offerLast(node.right); //深度优先遍历,先遍历左边,后遍历右边,栈先进后出
}
if(node.left!=null) {
deque.offerLast(node.left);
}
}
}
广度优先遍历BFS
- 遍历规则
先访问当前节点的所有邻接点,再返回父节点 - 层次遍历
从上到下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问节点,访问完一层就进入下一层,直到没有节点可以访问为止。 - 分析
保留全部节点,占用空间大;无回溯操作(即无入栈、出栈操作),运行速度快。 - Java代码
//广度优先遍历(优先队列)
public void BroadFirstSearch(TreeNode root) {
if(root==null) {
return;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()) {
TreeNode node=queue.poll();
System.out.print(node.data+" ");
if(node.left!=null) {
queue.offer(node.left); //广度优先遍历,每一行从左到右遍历
}
if(node.right!=null) {
queue.offer(node.right);
}
}
}
LeetCode经典题
从上到下打印二叉树
此系列对应剑指offer的32-I、II、III
- 32-I(BFS)
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。输出为一个数组
public int[] levelOrder(TreeNode root){
List<Integer> list = new ArrayList<>();
//BFS,优先队列存储
Queue<TreeNode> queue = new LinkedList<>();
if(root==null)return new int[0];
queue.add(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
list.add(node.val);
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
}
//list转换为数组形式
int[] ans = new int[list.size()];
for(int i = 0;i < list.size();i++){
ans[i] = list.get(i);
}
return ans;
}
- 32-II(BFS+双端队列)
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。输出为一个双重list数组
public List<List<Integer>> levelOrder1(TreeNode root){
List<List<Integer>> ans = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList<>();
if(root!=null)deque.offerLast(root);
while(!deque.isEmpty()){
List<Integer> tmp = new ArrayList<>();
for(int i = deque.size();i > 0;i--){
TreeNode node = deque.pollFirst();
tmp.add(node.val);
if(node.left!=null)deque.offerLast(node.left);
if(node.right!=null)deque.offerLast(node.right);
}
ans.add(tmp);
if(!deque.isEmpty())break;
}
return ans;
}
- 32-III(BFS+双端队列+奇偶逻辑分离)
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其它行依次类推。
public List<List<Integer>> levelOrder2(TreeNode root){
//奇偶逻辑分离
List<List<Integer>> ans = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList<>();
if(root!=null)deque.offerLast(root);
while(!deque.isEmpty()){
//从左往右打印奇数层
List<Integer> tmp = new ArrayList<>();
for(int i = deque.size();i > 0;i--){
TreeNode node = deque.pollFirst();
tmp.add(node.val);
if(node.left!=null)deque.offerLast(node.left);
if(node.right!=null)deque.offerLast(node.right);
}
ans.add(tmp);
if(deque.isEmpty())break;
//从右往左打印偶数层
tmp = new ArrayList<>();
for(int i = deque.size();i > 0;i--){
TreeNode node = deque.pollLast();
tmp.add(node.val);
if(node.right!=null)deque.offerFirst(node.right);
if(node.left!=null)deque.offerFirst(node.left);
}
ans.add(tmp);
if(deque.isEmpty())break;
}
return ans;
}
二叉(搜索)树的最近公共祖先
此系列对应剑指offer的68-I、II
- 68-I(利用二叉搜索树的性质)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
TreeNode ancestor = root;
while(true){
if(p.val < ancestor.val && q.val < ancestor.val)ancestor = ancestor.left;
else if(p.val < ancestor.val && q.val < ancestor.val)ancestor = ancestor.right;
else break;
}
return ancestor;
}
- 68-II(递归)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q){
if(root==null||root==p||root==q)return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left==null && right==null)return null;
else if(left!=null && right!=null)return root;
else return left==null?right:left;
}