树和二叉树
- 树的基本概念
线性结构中的一个结点至多只有一个直接后继,而树形结构中一个结点可以有一个或多个直接后继。因此,树形结构可以表示更复杂的数据
(1) 树的概念
- 1.1 树的相关术语(重点)
(1) 结点的度:
树上任一结点所拥有的子树的数目称为该结点的度
(2) 叶子:
(3) 树的度:
一颗树中所有结点的度的最大值称为该树的度
(4) 双亲结点:
父结点
(5) 结点的层次:
从根结点开始,根的层次为1,其余结点的层次为其双亲的层次加1
(6) 树的高度:
一颗树中所有结点层次数的最大值称为该树的高度(深度)
容易混淆的几个概念:层次一般是指某个结点在当前数中的第xxx层;而高度是指树的最大层次数,高度也称为深度
- 二叉树的基本概念
每个结点最多有2棵子树(二叉树的子树有左子树和右子树之分)
(1) 二叉树的5种基本形态(重点)
- 1.1 空树,没有任何结点
- 1.2 只有根
- 1.3 只有左子树
- 1.4 只有右子树
- 1.5 有左右子树
(2) 二叉树的基本运算
- 1.1 初始化:建立一颗空二叉树
- 1.2 求双亲(父结点)
- 1.3 求左孩子和右孩子
- 1.4 建立一颗二叉树
- 1.5 二叉树的遍历(先序、中序、后序、层次遍历)(重点)
1.先序遍历:中左右
2.中序遍历:左中右
3.后序遍历:左右中
(3) 二叉树的性质(重点)
性质1: 二叉树第i(i>=1)层上至多有2^(i-1)个结点
性质2: 深度为k(k>=1)的二叉树至多有(2^k)-1个结点,注意是树的总结点个数
深度计算示例:
深度为4的树的最多有1 + 2 + 4 + 8个结点
性质3: 对任何一颗二叉树,若度为0的结点(叶结点)个数为n0(0是下标),度数为2的结点个数为n2(2是下标),则n0 = n2 + 1
- 满二叉树和完全二叉树
-
1.1 满二叉树
深度为k(k >= 1)且有2^(k-1)个结点的二叉树称为满二叉树。满二叉树上的结点数已达到了二叉树可以容纳的最大值 -
1.2 完全二叉树
完全二叉树是在满二叉树上,从右到左,从下往上的去除结点,得到去除结点之后的树(结点之间必须连续),满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树 -
1.2.1 完全二叉树的性质(其中[x]表示不大于x的最大整数)
性质1:含有n个结点的完全二叉树的深度为[log₂ n] + 1
性质2:如果将一颗有n个结点的完全二叉树按层编号(将二叉树中的所有n个结点按第一层到最大层,每层从左到右的顺序依次标记尾1,2,...n),则对任一编号为i的结点A(1<=1<=n)有:
1. 若i = 1,则结点A是根:若i > 1,则A的双亲编号为[i / 2]
2. 若2*i>n,则结点A即无左孩子,也无右孩子;否则A的左孩子编号为2*i
3. 若2*i+1>n,则结点A无右孩子;否则,A的右孩子的编号为2*i+1
性质2表明完全二叉树上的结点之间的父子关系可由它们编号之间的关系来表达。性质2是二叉树顺序存储结构的基础
-
红黑树(课外延伸,非考点)
3.1 概念
红黑树是一颗特征的二叉树,具有平衡性质,查找的复杂度log(N)
3.2 性质
(1) 颜色由红色和黑色组成
(2) 根结点都是黑色
(3) 两个红色的节点不相邻
(4) 任一结点到叶子结点,有相同的高度
(5) 所有的叶子结点都是黑色的 -
B树与B+树(课外延伸,非考点)
-
二叉搜索树(二叉查找树)
搜索效率高,因为数据进行了排序,整个树的父结点大于左子树,小于又子树 -
平衡二叉树(课外延伸,非考点)
在二叉搜索树的基础上,具有自平衡功能,且左子树与右子树之间的高度差不超过1,它的左子树和右子树都是一颗平衡二叉树
-
6.1 平衡二叉树的不平衡的4种形态
(1) 全部往左偏
(2) 全部往又偏
(3) 往左偏 + 往又偏
(4) 往又偏 + 往左偏 -
6.2 平衡二叉树的旋转
旋转时需要保证树的顺序,以某个结点为轴进行旋转,旋转到左边或右边
(1) 右旋
(2) 左旋
(3) 左右旋
先左旋,然后右旋
(4) 右左旋
先右旋,再左旋
注意:判断是否是平衡二叉树不能只判断一部分是否平衡,而是要看整体的高度是否满足要求
- 代码实战
package org.springcloud.order.test.arithmetic;
import lombok.Data;
/**
* 完全二叉树的基本操作
*/
public class BinaryTree {
public static void main(String[] args) {
//数组下标0不使用,目的是为了更好的利用公式
Integer [] arr = {null,20,18,30,0,19,29,31};
Node node = build(arr);
System.out.println(node);
middleTraversal(node);
}
/**
* 根据数组构建完全二叉树
* */
public static Node build(Integer [] arr){
validate(arr);
//递归构建左右子树
Node rootNode = new Node(arr[1]);
Node node = build(rootNode,arr,1);
return node;
}
public static Node build(Node node,Integer [] arr,int i){
if(2 * i <arr.length){
int leftIndex = 2 * i;
Node left = new Node(arr[leftIndex]);
node.setLeft(left);
build(left,arr,leftIndex);
int rightIndex = 2 * i + 1;
Node right = new Node(arr[rightIndex]);
node.setRight(right);
build(right,arr,rightIndex);
}
return node;
}
public static void validate(Integer [] arr){
Integer rootNode = arr[1];
if(rootNode == null){
throw new RuntimeException("没有根结点");
}
if(rootNode <1){
throw new RuntimeException("结点值不能小于1");
}
}
/**
* 先序遍历
*/
public static void prevTraversal(Node node){
if(node != null){
System.out.println(node.data);
prevTraversal(node.left);
prevTraversal(node.right);
}
}
/**
* 中序遍历
* @param node
*/
public static void middleTraversal(Node node){
if(node != null){
middleTraversal(node.left);
System.out.println(node.data);
middleTraversal(node.right);
}
}
@Data
public static class Node{
public Node(Integer data){
this.data = data;
}
//数据域
private Integer data;
//双亲结点
private Node parent;
//左子树
private Node left;
//右子树
private Node right;
}
}