手写红黑树
前言
之前也写了一些数据结构但都写完就丢。没记录下来。感觉有点可惜,所以这次想记录一下。本次这个红黑树删除采用的不是传统的和叶子节点交换。而是通过合并两个子树来删除。我借鉴一篇haskell的红黑树。不知道是不是我没仔细看,感觉那篇红黑树实现起来有些漏洞。我上一篇博文也有点。虽然修补一下也没问题。但改来改去就是让人不爽。最后我放弃了blance高度必须减一的性质。思路就清晰多了。
代码
AVL
package cn.lyf.tree;
public class AVL<T extends Comparable<T>> {
Node<T> root;
Node<T> roate(Node<T> n, boolean left) {
if (n == null)
return null;
Node<T> t1 = n.get(!left), t2;
if (t1 == null)
return n;
t2 = t1.get(left);
n.set(!left, t2);
t1.set(left, n);
return t1;
}
}
Node
package cn.lyf.tree;
class Node<T extends Comparable<T>> {
T val;
boolean red;
Node<T> L, R;
Node(T v) {
val = v;
}
Node(Node<T> L, T val, Node<T> R, boolean red) {
this.val = val;
this.L = L;
this.R = R;
this.red = red;
}
Node<T> get(boolean left) {
if (left)
return L;
return R;
}
Node<T> set(boolean left, Node<T> v) {
if (left)
return L = v;
return R = v;
}
}
RedBlackTree
package cn.lyf.tree;
public class RedBlackTree<T extends Comparable<T>> extends AVL<T> {
boolean[][] paths = { { true, false }, { false, true }, { true, true }, { false, false } };
Node<T> dyeing(Node<T> n) {
if (n.red)
return n;
boolean[] lefts = new boolean[2];
if (!canDyeing(n, lefts))
return n;
if (lefts[0] ^ lefts[1])
n.set(lefts[0], roate(n.get(lefts[0]), lefts[0]));
n = roate(n, !lefts[0]);
n.red = true;
n.L.red = n.R.red = false;
return n;
}
Node<T> ins(Node<T> n, T k) {
if (n == null)
return new Node<T>(null, k, null, true);
if (k == n.val)
return n;
if (k.compareTo(n.val) < 0)
n.L = dyeing(ins(n.L, k));
else
n.R = dyeing(ins(n.R, k));
return n;
}
Node<T> merge(Node<T> a, Node<T> b) {
if (a == null)
return b;
if (b == null)
return a;
if (a.red && b.red) {
Node<T> s = merge(a.R, b.L);
if (s == null || !s.red) {
a.R = s;
b.L = a;
return b;
}
a.R = s.L;
b.L = s.R;
s.L = a;
s.R = b;
return s;
}
// 双黑
if (!a.red && !b.red) {
a.R = merge(a.R, b.L);
b.L = a;
a.red = true;
return dyeing(b);
}
// 异色
if (a.red) {
a.R = merge(a.R, b);
return a;
} else {
b.L = merge(a, b.L);
return b;
}
}
// 优先左右,或右左
boolean canDyeing(Node<T> n, boolean[] lefts) {
for (boolean[] path : paths) {
Node<T> t = n.get(path[0]);
if (t == null || !t.red)
continue;
t = t.get(path[1]);
if (t == null || !t.red)
continue;
lefts[0] = path[0];
lefts[1] = path[1];
return true;
}
return false;
}
Msg blance(Node<T> n, boolean left) {
Node<T> t = n.get(!left);
// 父节点为红色
if (n.red) {
t.red = true;
n.red = false;
return new Msg(dyeing(n), true);
}
// 父节点为黑色
// 兄弟节点为黑色
if (!t.red) {
t.red = true;
n = dyeing(n);
if (n.red) {
n.red = false;
return new Msg(n, true);
}
return new Msg(n, false);
}
// 兄弟节点为红色
n.set(!left, roate(t, !left));
n = roate(n, left);
t.red = false;
t.get(!left).red = true;
n.set(!left, dyeing(t));
return new Msg(n, true);
}
Msg del(Node<T> n, T k) {
if (n == null)
return new Msg(null, true);
if (n.val.equals(k)) {
Node<T> t = merge(n.L, n.R);
// 删除的是红色节点
if (n.red)
return new Msg(t, true);
// 删除的是黑色节点
// 合并后根节点为红色
if (t != null && t.red) {
t.red = false;
return new Msg(t, true);
}
// 合并后根节点为null或黑色
return new Msg(t, false);
}
boolean ju = n.val.compareTo(k) > 0;
Msg res = ju ? del(n.L, k) : del(n.R, k);
n.set(ju, res.node);
if (res.isBlance)
return new Msg(n, true);
return blance(n, ju);
}
public void insert(T k) {
root = ins(root, k);
root.red = false;
root = dyeing(root);
root.red = false;
}
public void delete(T k) {
root = del(root, k).node;
}
class Msg {
Node<T> node;
boolean isBlance;
Msg(Node<T> n, boolean j) {
node = n;
isBlance = j;
}
}
}
TreeUtil
package cn.lyf.tree;
public class TreeUtil {
<T extends Comparable<T>> void toMermaid(Node<T> n, StringBuffer sb) {
if (n == null)
return;
toMermaid(n.L, sb);
String name = n.toString().replace("@", "");
sb.append(name + "(('" + n.val + "'))\n");
sb.append("\nclass " + name + " " + (n.red ? "red" : "black"));
if (n.L != null)
sb.append("\n" + name + "-->" + n.L.toString().replace("@", ""));
if (n.R != null)
sb.append("\n" + name + "-->" + n.R.toString().replace("@", ""));
sb.append("\n");
toMermaid(n.R, sb);
}
<T extends Comparable<T>> String toMermaid(Node<T> r) {
StringBuffer sb = new StringBuffer();
sb.append(
"\n```mermaid\ngraph TD\nclassDef black fill:#222,color:#fff,stroke:#000,stroke-width:2px;\nclassDef red fill:#f22,color:#fff,stroke:#000,stroke-width:2px;\n");
toMermaid(r, sb);
sb.append("\n```\n");
return sb.toString();
}
}
RBTest
package cn.lyf.tree;
import java.util.*;
public class RBTest<T extends Comparable<T>> {
public boolean isRB(Node<T> n) {
return (Boolean) isRB(n, true)[0];
}
void addALL(Node<T> n, List<T> l) {
if (n == null)return;
addALL(n.L, l);
l.add(n.val);
addALL(n.R, l);
}
public boolean isOrder(TreeSet<Integer> s, Node n) {
List<Integer> l1 = new LinkedList<>(s);
List<Integer> l2 = new LinkedList<>();
addALL(n, l2);
return l1.equals(l2);
}
Object[] isRB(Node<T> n, boolean red) {
if (n == null)
return new Object[] { true, 0 };
if (red && n.red)
return new Object[] { false, 0 };
int h = n.red ? 0 : 1;
Object[] r1 = isRB(n.L, n.red);
Object[] r2 = isRB(n.R, n.red);
if ((Boolean) r1[0] && (Boolean) r2[0] && (int) r1[1] == (int) r2[1])
return new Object[] { true, (int) r1[1] + h };
return new Object[] { false, 0 };
}
}
TreeUtil 类比较有趣。用来可视化树。你可以把字符直接粘贴到Typora就可以显示了。