目录
1. 数据结构树–>树基础
2. 数据结构树–>二叉树
3. 数据结构树–>二叉查找树\二叉排序树
4. 数据结构树–>平衡二叉树
5. 数据结构树–>霍夫曼树
6. 数据结构树–>红黑树
7. 数据结构树–>二叉堆
8. 数据结构树–>B树
9. 数据结构树–>B+树
二叉查找树\二叉排序树
二叉查找树是一种有序的二叉树,也叫儿叉排序树,它在二叉树的基础上添加了位置排序的规则。
规则: 每颗子树的根节点值比左子树大,比右子树小。
二叉查找树的缺点: 二叉查找树有了排序,但当我们的数据顺序与大小规律一致是就会发现,二叉查找树基本退化为了链表,导致达不到二叉
查找树快速查找的目的。
后续我们在介绍平衡二叉树的时候会解决之一问题。
1. 二叉查找树的查找
更具儿叉查找树的特性二叉查找树查找元素快速简单,快速:二叉查找树查找基本就是二分查找,所以快速。
2. 二叉查找树的插入
首先二叉查找树新插入的节点必定是新叶子节点。
如图,节点6并不会插入到节点5与节点7之间,而是插入到节点5的右孩子节点,新的节点是新叶子节点。
3. 二叉查找树的删除
二叉查找树的删除就有点复杂了,删除节点会破坏二叉查找树的结构,我们删除完节点后需要恢复该结构,在充分理解了二叉树的特性后你又能很容易的理解。
需要理解的特性:每颗子树的根节点的值比左子树大,比右子树小。
- 根据特性1,我们能推倒出每颗子树可以一直向左查找,查到叶子节点就是该子树的最小值,一直向右查找,查到叶子节点就是该子树的最大值。
二叉查找树的删除分三种情况:
- 删除的节点是叶子节点。
- 删除的节点只有一个子树。
- 删除的节点有两个子树。
3.1 删除的节点是叶子节点
这个很简单,我们了解叶子节点的特性,叶子节点可以直接删除不会破坏查找树的结构。
3.2 删除的节点只有一个子树
我们了解的二叉查找树的特性,如果一个节点只有一个子树,那么这个子树不管是左子树还是右子树,这个子树直接赋值到该节点的父节点上也是不会破坏
查找树的结构的。
如图删除节点4,根据二叉查找树排序的特性我们知道节点4的子树相对于它的父节点的排序与结构特性也是符合的,所以我们就可以将节点4的子树直接按照
节点4相对于节点4的父节点的左右区分赋值给节点4的父节点。也就是节点4属于父节点的左子树,删除节点4,节点4的子树赋值给父节点的左子树。
节点4属于父节点的右子树,子树就赋值给父节点的右子树,不用区分子树是属于节点4的左子树还是右子树。
3.3 删除的节点有两个子树
如图当我们要删除节点3的时候就会破坏二叉查找树的结构,我们删除后需要恢复该节点的结构。怎么恢复呢?需要确认那个节点来代替节点3的位置,如果使用右子树根节点5
来代替那节点4就需要动,使用左子树同样会出现这个问题。
我们了解二叉查找树的特性,每颗子树的根节点大于所有左子树,小于所有右子树,我们需要在左右子树中找出符合这个特性的节点来替代节点3,这个替代过程也要简单无需移动
太多。符合这个要求的节点我们根据二叉查找树的特性就能找到,最好的节点就是要删除的节点3的左子树最大值,右子树的最小值,也就是左子树的最右叶子节点,右子树的最左
叶子节点。我们通常使用的是右子树的最左节点(最小值)来替代节点3。
3.4 java 代码实现
public static void delete(BinaryTreeNode root, int key) {
BinaryTreeNode father = root;
BinaryTreeNode delete = null;
//delete节点属于father的那个方向
boolean left = true;
while (father != null) {
if (father.getKey() < key) {
if (father.getRight() != null && father.getRight().getKey() == key) {
delete = father.getRight();
left = false;
break;
} else {
father = father.getRight();
}
} else {
if (father.getLeft() != null && father.getLeft().getKey() == key) {
delete = father.getLeft();
left = true;
break;
} else {
father = father.getLeft();
}
}
}
if (father != null) {
System.out.println("father key=" + father.getKey() + " delete key=" + delete.getKey());
BinaryTreeNode l = delete.getLeft();
BinaryTreeNode r = delete.getRight();
if (l == null && r == null) {
//删除的节点是叶子节点
if (left) {
father.setLeft(null);
} else {
father.setRight(null);
}
} else if (l == null || r == null) {
//删除的节点只有一个子树
if (left) {
if (l == null) {
father.setLeft(delete.getRight());
} else {
father.setLeft(delete.getLeft());
}
} else {
if (l == null) {
father.setRight(delete.getRight());
} else {
father.setRight(delete.getLeft());
}
}
} else {
//删除的节点有两个子树
//找到右子树的最小值
BinaryTreeNode tempFather = delete;
BinaryTreeNode min = null;
Stack<BinaryTreeNode> temps = new Stack<>();
temps.add(tempFather);
tempFather = tempFather.getRight();
while (true) {
temps.push(tempFather);
if (tempFather.getLeft() == null) {
break;
} else {
tempFather = tempFather.getLeft();
}
}
min = temps.pop();
tempFather = temps.pop();
if (left) {
father.setLeft(min);
} else {
father.setRight(min);
}
if (temps.size() > 0) {
//这个判断 tempFather 不是为delete节点 如果最小值有右子树则赋值右子树。
if (min.getRight() != null) {
tempFather.setLeft(min.getRight());
}
min.setLeft(delete.getLeft());
min.setRight(delete.getRight());
} else {
if (min.getRight() != null) {
tempFather.setRight(min.getRight());
}
min.setLeft(delete.getLeft());
}
}
}
}