这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战
二叉搜索树
根据comparator进行比较,
大的在左边或大的在右边
实现代码
public class BSTree {
BSTreeNode tree;
public void insert(int val){
insertVal(tree,val);
}
public boolean findVal(int val){
return findNode(val) != null;
}
private BSTreeNode findMinNode(BSTreeNode head){
while(head!=null) head = head.left;
return head;
}
private BSTreeNode findMaxNode(BSTreeNode head){
while(head.right!=null) head = head.right;
return head;
}
BSTreeNode findNode(int val){
BSTreeNode cur = tree;
while(cur !=null){
if(val == cur.val) return cur;
if(val < cur.val) cur = cur.left;
else cur = cur.right;
}
return null;
}
}
节点定义:
private class BSTreeNode {
int val;
BSTreeNode left;
BSTreeNode right;
BSTreeNode prev;
public BSTreeNode(int val) {
this.val = val;
}
}
插入:
- 插入的操作其实很简单,只要根据大小,循环往下,找到为null的地方赋值即可。
void insertVal(BSTreeNode head, int val){
BSTreeNode levelPrev = head,level = head;
while(null != level){
levelPrev = level;
if(level.val <= val){
level = level.right;
}else{
level = level.left;
}
}
level = new BSTreeNode(val);
level.prev = levelPrev;
if(levelPrev.val < val) levelPrev.right = level;
else levelPrev.left = level;
}
删除:
-
删除第一步,找到节点,再根据节点状态进行下一步处理
-
删除替代节点的查找:
-
左边最大的,或者右边最小的,其实都是可以的
- 左边最大的:左子树的右右..右节点
- 右边最小的:右子树的左左..左节点
-
-
public void remove(int val){
BSTreeNode removeNode = findNode(val);
if(null == removeNode) return;
if(null == removeNode.prev){//root - make it null
tree = null;
return;
}
boolean left = removeNode.val <= removeNode.prev.val;
BSTreeNode preNode = removeNode.prev;
if(null == removeNode.left && null == removeNode.right){//both null
if(left) removeNode.prev.left = null;
else removeNode.prev.right = null;
return;
}
if(null == removeNode.left || null == removeNode.right){//either is null - change ptr only
if(left) preNode.left = null == removeNode.left?removeNode.right:removeNode.left;
else preNode.right = null == removeNode.left?removeNode.right:removeNode.left;
}else{//both exist
if(left) {//left
//can be replaced by findMaxNode(removeNode.left)
BSTreeNode replacement = findMinNode(removeNode.right);
BSTreeNode replacementPre = replacement.prev;
replacement.prev = preNode;
preNode.left = replacement;
//
replacementPre.left = replacement.right;
replacement.right = removeNode.right;
replacement.left = removeNode.left;
}
else{//right
BSTreeNode replacement = findMaxNode(removeNode.left);
BSTreeNode replacementPre = replacement.prev;
replacement.prev = preNode;
preNode.right = replacement;
//
replacementPre.right = replacement.left;
replacement.left = removeNode.left;
replacement.right = removeNode.right;
}
}
}
红黑树
红黑树定义和性质
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:
- 性质1:每个节点要么是黑色,要么是红色。
- 性质2:根节点是黑色。
- 性质3:每个叶子节点(NIL)是黑色。
- 性质4:每个红色结点的两个子结点一定都是黑色。
- 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
相关关系描述
P
/ \
PL PR
/ \
C B
B为插入且未平衡的节点,则:
- P为祖父节点
- PR为父节点
- PL为伯父节点
HashMap中RBTree的实现
在java-hashMap的实现中:
- 节点的红黑值是通过boolean值来确定的,因此性质1是不需要检查的
- TreeNode的节点初始化时,左右节点都是null,null值默认是黑,因此性质3也不需要检查
- 在平衡检查中,预计插入的节点一开始就赋值为红
结构操作
左旋
- 左旋
A(pp) A(pp)
| |
B(p) --------- >>>> C(r)
\ /
C(r) B(p)
/ \
D(RL) D(rl)
左旋就是将原本的右侧子节点作为父节点,左旋就意味着将旋转的节点变成了左节点
右旋
pp pp
| |
P --------------》 L
/ \
L P
\ /
LR LR
类似上面的,右转就是把当前节点和左子节点地位互换,其他地方的结构不变。