2-3查找树
2-结点:含有一个键(及其对应的值)和两条链,左链接指向2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点:含有两个键(及其对应的值)和三条链,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。
查找:判断一个键是否在树中,先和根节点的键比较,如果相等,查找命中,如果不同,根据比较结果,在其子树中继续查找。还是空连接,查找未命中。
插入:
1.向2-结点插入:首先进行查找,将结点挂载未找到的结点上,如果未找到的结点是一个2-结点,将新元素放到里面变成3-结点。
2.向3-结点插入:将元素放入3-节点,变成4-结点,将4-结点中间元素提升,小于中间元素作为左节点,大于中间元素作为右结点。树的高度加1。
3.向父节点为2-结点,子结点为3-结点中插入:将元素插入3-结点中,变成临时的4-结点。将结点中的中间元素提升到2-结点中,父节点从2-结点变成3-结点,将左右元素挂载到适当的位置。
4.向父子结点为3-结点中插入:将元素插入子结点3-结点中,变成临时的4-结点。提升中间元素将父节点从3-结点变成4-结点,将左右元素放到适当位置。将父节点中的中间元素提升,直到遇到一个父节点是2-结点,将其变成3-结点为止,就可以了。
5.当插入时,所有结点都是3-结点时,将根节点变成一个临时4-结点,将根节点拆分成两个2-结点。树高度+1.
性质:
1.任意空链接到根结点的路径长度都是相等的。
2. 4-结点变换为3-结点时,树的高度不会发生变化,只有当根结点是临时的4-结点,分解根结点时,树高+1。
3. 2-3树与普通二叉查找树最大的区别在于,普通的二叉查找树是自顶向下生长,而2-3树是自底向上生长。
直接实现2-3查找树较为复杂,但是其概念有利于红黑树、B树、B+树。
红黑树
红黑树主要是对2-3树进行编码,红黑树背后的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。我们将树中的链接分为两种类型:
红链接:将两个2-结点连接起来构成一个3-结点; 黑链接:则是2-3树中的普通链接。
红黑树定义:
1. 红链接均为左链接;
2. 没有任何一个结点同时和两条红链接相连;
3. 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同;
其实就是将3-结点中的两个键分开,中间用红色连接
private class Node<Key,Value>{
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
//由其父结点指向它的链接的颜色
public boolean color;
public Node(Key key, Value value, Node left,Node right,boolean color) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.color = color;
}
}
对红黑树进行增删改时,可能出现两条红链接,需要进行旋转修复。
1.左旋
场景:某个结点的左子节点为黑色,右子节点为红色,需要左旋
左旋过程:
1.让x的左子结点变为h的右子结点:h.right=x.left;
2.让h成为x的左子结点:x.left=h;
3.让h的color属性变为x的color属性值:x.color=h.color;
4.让h的color属性变为RED:h.color=true;
2.右旋
场景:某个结点的左子节点以及左子节点的左子节点都是红链接。
右旋过程:
1. 让x的右子结点成为h的左子结点:h.left = x.right;
2. 让h成为x的右子结点:x.right=h;
3. 让x的color变为h的color属性值:x.color = h.color;
4. 让h的color为RED;
插入
1.向单个2-结点中插入:新键小于当前结点的键时,新增红色结点。如果大于新增红色右链接,左旋。
2.向底部的2-结点插入:在树的底部新增一个结点,通过红链接相连,父节点为2-结点时,上面方法使用。
3.颜色反转:当一个结点的左右子结点为红链接时,将左右链接变成黑链接,与父节点的链接变成红链接。
4.向3-结点中插入:大于结点中的两个键时,向右插入,颜色反转;小于两个键时,向左插入,右旋,颜色反转;介于两个键中间,在中间插入,左旋,右旋,颜色反转。
5.向底部3-结点插入:新节点链接是3-结点的右链接,只需要转换颜色;左链接时,右旋,颜色转换;中链接时,左旋,右旋,颜色转换。
左旋:
private Node rotateLeft(Node h) {
//找出当前结点h的右子结点
Node hRight = h.right;
//找出右子结点的左子结点
Node lhRight = hRight.left;
//让当前结点h的右子结点的左子结点成为当前结点的右子结点
h.right = lhRight;
//让当前结点h称为右子结点的左子结点
hRight.left = h;
//让当前结点h的color编程右子结点的color
hRight.color = h.color;
//让当前结点h的color变为RED
h.color = RED;
//返回当前结点的右子结点
return hRight;
}
右旋:
private Node rotateRight(Node h) {
//找出当前结点h的左子结点
Node hLeft = h.left;
//找出当前结点h的左子结点的右子结点
Node rHleft = hLeft.right;
//让当前结点h的左子结点的右子结点称为当前结点的左子结点
h.left = rHleft;
//让当前结点称为左子结点的右子结点
hLeft.right = h;
//让当前结点h的color值称为左子结点的color值
hLeft.color = h.color;
//让当前结点h的color变为RED
h.color = RED;
//返回当前结点的左子结点
return hLeft;
}
颜色反转:
private void flipColors(Node h) {
//当前结点的color属性值变为RED;
h.color = RED;
//当前结点的左右子结点的color属性值都变为黑色
h.left.color = BLACK;
h.right.color = BLACK;
}