-
获取红黑树节点在二维数组中的(理论)纵坐标,(即不考虑排版用到的”/“,""所占用的纵向坐标)
-
求某节点的纵坐标,即求该节点有多少个直接或间接的父节点
-
@param node 红黑树节点
-
@return 纵坐标
*/
private int getNodeY(Node<K, V> node) {
int y = 0;
if (node != null) {
Node<K, V> p = node.parent;
if (p == null) {
return y;
}
y++;
while ((p = p.parent) != null) {
y++;
}
}
return y;
}
/**
-
将红黑树节点加入二维数组对应坐标中
-
@param node 红黑树节点
-
@param rbTree 二维数组
*/
private void setNodeIntoArr(Node<K, V> node, Node[][] rbTree) {
int x = getNodeX(node);
int y = getNodeY(node);
rbTree[y * 2][x] = node;//实际纵坐标不是y,而是y*2,因为纵向加入了"/“,”"
if (node.parent != null) {
if (node == node.parent.left) {
rbTree[y * 2 - 1][x] = new Node<String, String>(“/”, null, null);
} else {
rbTree[y * 2 - 1][x] = new Node<String, String>(“\”, null, null);
}
}
}
/**
-
打印红黑树
-
将红黑树放进二维数组中,这个二维数组的行数是红黑树的高度*2-1(*2-1的原因是给子节点加入了”/“,”\“指向),列数是红黑树的节点个数。
-
红黑树节点在二维数组中的行(纵坐标)列(横坐标)计算:
*/
public void showRBTree() {
if(root==null){
System.out.println(“当前红黑树为空树”);
return;
}
int origHeight = getHeight(root);
int height = origHeight * 2 - 1;
int width = size;
Node<K, V>[][] rbTree = new Node[height][width];
// 将根节点加入二维数组
setNodeIntoArr(root, rbTree);
// 将根节点的左子树加入二维数组
Node<K, V> prev = prevNode(root);
while (prev != null) {
setNodeIntoArr(prev, rbTree);
prev = prevNode(prev);
}
// 将根节点的右子树加入二维数组
Node<K, V> next = nextNode(root);
while (next != null) {
setNodeIntoArr(next, rbTree);
next = nextNode(next);
}
/**
- 遍历包含红黑树节点的二维数组,此时红黑树节点已经按照对应的坐标加入二维数组
*/
for (int i = 0; i < rbTree.length; i++) {
for (int j = 0; j < rbTree[i].length; j++) {
Node<K, V> node = rbTree[i][j];
if (node == null) {
System.out.print(“\t”);
} else {
if (node.color == RED) {
if (node.key.equals(“/”) || node.key.equals(“\”)) {
System.out.print(“\t” + ConsoleColors.BLUE + node.key + ConsoleColors.RESET);
} else {
System.out.print(“\t” + ConsoleColors.RED + node.key + ConsoleColors.RESET);
}
} else {
System.out.print(“\t” + ConsoleColors.BLACK + node.key + ConsoleColors.RESET);
}
}
}
System.out.println();
}
}
static class Node<K, V> {
K key;
V value;
Node<K, V> parent;
Node<K, V> left;
Node<K, V> right;
boolean color;
public Node(K key, V value, Node<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
}
/**
-
左旋
-
@param node 旋转点
*/
public void rotateLeft(Node<K, V> node) {
if (node == null)
return;
Node<K, V> r = node.right;
Node<K, V> rl = r.left;
Node<K, V> p = node.parent;
if (p != null) {
if (p.left == node) {
p.left = r;
} else {
p.right = r;
}
} else {
root = r;
}
r.parent = p;
r.left = node;
node.parent = r;
node.right = rl;
if (rl != null) {
rl.parent = node;
}
}
/**
-
右旋
-
@param node 旋转点
*/
public void rotateRight(Node<K, V> node) {
if (node == null)
return;
Node<K, V> p = node.parent;
Node<K, V> l = node.left;
Node<K, V> lr = l.right;
if (p != null) {
if (p.left == node) {
p.left = l;
} else {
p.right = l;
}
} else {
root = l;
}
l.parent = p;
l.right = node;
node.parent = l;
node.left = lr;
if (lr != null) {
lr.parent = node;
}
}
/**
-
红黑树插入节点
-
如果是空树的话,则插入节点作为根节点
-
如果是非空树,则从根节点开始比较,
-
如果比较结果小于0,则说明插入节点在比较节点的左子树中,继续比较
-
如果比较结果大于0,则说明插入节点在比较节点的右子树中,继续比较
-
如果比较结果等于0,则说明插入节点对应的key已存在,只需要替换value即可
-
比较直至节点的左子树或右子树为null
-
@param key
-
@param value
-
@return 覆盖的value
*/
public V put(K key, V value) {
Node<K, V> node = root;
Node<K, V> newNode;
/**
- 空树
*/
if (node == null) {
// 插入节点作为根节点
root = new Node<K, V>(key, value, null);
newNode = root;
size++;
}
/**
- 非空树
*/
else {
int cmp;
Node<K, V> parent;
//从根节点开始比较
do {
parent = node;
// key的类型是K extends Comparable,即key的类型必须实现Compareable接口,且自然排序的元素是K
cmp = key.compareTo(node.key);
if (cmp < 0) {
// 如果插入的key小于当前比较节点的key,则继续和当前节点的左子节点比较
node = node.left;
} else if (cmp > 0) {
// 如果插入的key大于当前比较节点的key,则继续和当前节点的右子节点比较
node = node.right;
} else {
// 如果插入的key和当前比较节点的key相同,则覆盖对应value
V oldValue = node.value;
node.value = value;
return oldValue;
}
} while (node != null);
// 如果上面逻辑比较到叶子节点还没有发现相同key的节点,则新增节点,新增节点的父节点是当前比较节点
newNode = new Node<K, V>(key, value, parent);
// 建立当前比较节点和新增节点的联系
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
}
fixAfterInsertion(newNode);
size++;
return null;
}
private boolean colorOf(Node<K, V> node) {
return node == null ? BLACK : node.color;
}
private void setColor(Node<K, V> node, boolean color) {
if (node != null) {
node.color = color;
}
}
private Node<K, V> parentOf(Node<K, V> node) {
return node == null ? null : node.parent;
}
private Node<K, V> grandOf(Node<K, V> node) {
return node == null ? null : parentOf(parentOf(node));
}
private Node<K, V> leftOf(Node<K, V> node) {
return node == null ? null : node.left;
}
private Node<K, V> rightOf(Node<K, V> node) {
return node == null ? null : node.right;
}
private void fixAfterInsertion(Node<K, V> newNode) {
newNode.color = RED;
// 只有新增节点的父节点是红色时,才需要调整
while (newNode != root && colorOf(parentOf(newNode)) == RED) {
// 判断新增节点是否有叔叔节点,右叔叔,左爸爸
if (leftOf(grandOf(newNode)) == parentOf(newNode)) {
Node<K, V> uncleNode = rightOf(grandOf(newNode));
// 没有叔叔节点,则为234树3节点添加元素
if (colorOf(uncleNode) == BLACK) {
// 非左倾结构
if (newNode != leftOf(parentOf(newNode))) {
// 转成左倾结构
rotateLeft(parentOf(newNode));
newNode = leftOf(newNode);
}
// 左倾结构
setColor(grandOf(newNode), RED);
setColor(parentOf(newNode), BLACK);
rotateRight(grandOf(newNode));
}
// 有叔叔节点,则为234树4节点添加元素
else {
setColor(parentOf(newNode), BLACK);
setColor(uncleNode, BLACK);
setColor(grandOf(newNode), RED);
newNode = grandOf(newNode);
}
}
// 判断新增节点是否有叔叔节点,左叔叔,右爸爸
else {
Node<K, V> uncleNode = leftOf(grandOf(newNode));
// 没有叔叔节点,则为234树3节点添加元素
if (colorOf(uncleNode) == BLACK) {
// 非右倾结构
if (newNode != rightOf(parentOf(newNode))) {
// 转成右倾结构
rotateRight(parentOf(newNode));
newNode = rightOf(newNode);
}
// 右倾结构
setColor(grandOf(newNode), RED);
setColor(parentOf(newNode), BLACK);
rotateLeft(grandOf(newNode));
}
// 有叔叔节点,则为234树4节点添加元素
else {
setColor(parentOf(newNode), BLACK);
setColor(uncleNode, BLACK);
setColor(grandOf(newNode), RED);
newNode = grandOf(newNode);
}
}
}
setColor(root, BLACK);
}
/**
-
获取当前节点的前驱节点
-
@param node 当前节点
-
@return 前驱节点
*/
private Node<K, V> prevNode(Node<K, V> node) {
// 如果当前节点不存在,则不存在前驱节点
if (node == null)
return null;
// 如果当前节点有左子树,则其前驱节点是其左子树中的最大值节点
Node prev;
if ((prev = node.left) != null) {
while (prev.right != null) {
prev = prev.right;
}
return prev;
}
// 如果当前节点没有左子树,则其前驱节点是其向上递归第一次左拐时的父节点
else {
Node p = node.parent;
Node c = node;
while (p != null && p.left == c) {
c = p;
p = p.parent;
}
return p;
}
}
/**
-
查找当前节点的后继节点
-
@param node 当前节点
-
@return 后继节点
*/
private Node<K, V> nextNode(Node<K, V> node) {
// 当前节点不存在,则不存在后继节点
if (node == null)
return null;
// 如果当前节点存在右子树,则其后继节点是其右子树中最小值节点
Node next;
if ((next = node.right) != null) {
while (next.left != null) {
next = next.left;
}
return next;
}
// 如果当前节点不存在右子树,则其后继节点是其向上递归第一次右拐时的父节点
else {
Node p = node.parent;
Node c = node;
while (p != null && p.right == c) {
c = p;
p = p.parent;
}
return p;
}
}
/**
-
根据key删除红黑树中对应的节点
-
@param key 要删除节点对应的key
-
@return 被删除节点的value
*/
public V remove(K key) {
// 先根据key找到要删除的节点
Node<K, V> node = getNode(key);
V oldValue = null;
if (node != null) {
oldValue = node.value;
// 删除节点
deleteNode(node);
}
return oldValue;
}
/**
-
根据key找对应节点
-
@param key 要找节点的key
-
@return 要找的节点
*/
private Node<K, V> getNode(K key) {
// 由于这里只有自然排序,所以key==null时无法使用compareTo方法
if (key != null) {
// 从红黑树根节点开始比较节点值大小
Node<K, V> x = root;
int cmp;
while (x != null) {
cmp = key.compareTo(x.key);
if (cmp < 0) {
x = x.left;
} else if (cmp > 0) {
x = x.right;
} else {
// 如果找到key相同的节点,则返回该节点
return x;
}
}
//走到此步,则说明没有找到key相同的节点,则返回null
}
return null;
}
/**
-
删除节点
-
@param node 删除的节点
*/
private void deleteNode(Node<K, V> node) {
if (node == null) {
return;
}
// 删除节点是双子节点,则使用它的前驱或后继节点来代替它,这里模仿TreeMap,使用后继节点来代替他
if (node.left != null && node.right != null) {
Node<K, V> replace = prevNode(node);
node.key = replace.key;
node.value = replace.value;
node = replace;//实际删除后继节点,且后继节点只可能是叶子节点或单子节点,不可能是双子节点
}
// 删除节点是单子节点
if ((node.left != null && node.right == null) || (node.left == null && node.right != null)) {
Node<K, V> replace;
if (node.left != null) {
replace = node.left;
} else {
replace = node.right;
}
node.key = replace.key;
node.value = replace.value;
node = replace;//实际删除单子节点的唯一子节点,且唯一子节点只能是叶子节点
}
// 删除节点是叶子节点
if (node.left == null && node.right == null) {
Node<K, V> p = node.parent;
if (p == null) {//如果叶子节点没有父节点,则说明该节点就是根节点,删除根节点后,不需要任何调整,因为已经是空树了
root = null;
size–;
return;
}
if (colorOf(node) == BLACK) {
fixAfterDeletion(node);
}
//解除叶子节点和其父节点的双向联系,方便叶子节点被垃圾回收
if (p.left == node) {
p.left = null;
} else {
p.right = null;
}
node.parent = null;
}
}
/**
-
删除节点后,调整红黑树
-
如果被删除的节点是红色的,则直接删除,无需调整
-
如果被删除的节点是黑色,
-
被删除节点是其父节点的左孩子
-
判断其兄弟节点是否为红色
-
若为红色,则为假兄弟节点,需要将假兄弟节点变黑,父节点变红,绕父节点左旋,而后得到真兄弟节点
-
若为黑色,则为真兄弟节点
-
判断其兄弟节点有几个孩子
-
若没有孩子
-
则兄弟节点没的借,兄弟节点自损变红
-
判断其父节点是否为红色,
-
若为红色,则变黑,结束调整
-
若为黑色,则继续将父节点当成被删除节点,从头开始循环,直到父节点为root或者父节点为黑色,结束循环
-
若有孩子
-
则判断兄弟节点右孩子是否为黑色
-
若为黑色,则将其兄弟节点变红,其兄弟右孩子变黑,绕兄弟节点右旋,重新获得兄弟节点
-
若为红色,则无需调整
-
其兄弟节点变为父节点颜色
-
其父节点变为黑色
-
其兄弟右孩子变为黑色
-
绕其父节点左旋
-
@param node 将要被节点
*/
private void fixAfterDeletion(Node<K, V> node) {
while(node!=root&&colorOf(node)==BLACK){
if (node==leftOf(parentOf(node))){
Node<K,V> brother = rightOf(parentOf(node));
if (colorOf(brother)==RED){
setColor(brother,BLACK);
setColor(parentOf(node),RED);
rotateLeft(parentOf(node));
brother = rightOf(parentOf(node));
}
if (colorOf(leftOf(brother))==BLACK&&colorOf(rightOf(brother))==BLACK){
setColor(brother,RED);
node = parentOf(node);
} else {
if (colorOf(rightOf(brother))==BLACK){
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
de)==BLACK){
if (node==leftOf(parentOf(node))){
Node<K,V> brother = rightOf(parentOf(node));
if (colorOf(brother)==RED){
setColor(brother,BLACK);
setColor(parentOf(node),RED);
rotateLeft(parentOf(node));
brother = rightOf(parentOf(node));
}
if (colorOf(leftOf(brother))==BLACK&&colorOf(rightOf(brother))==BLACK){
setColor(brother,RED);
node = parentOf(node);
} else {
if (colorOf(rightOf(brother))==BLACK){
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-oAtUOAR1-1715795266137)]
[外链图片转存中…(img-qEWt73KE-1715795266137)]
[外链图片转存中…(img-YCsE32O3-1715795266137)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!