之前分析了红黑树的删除,这里附上红黑树的完整版代码,包括查找、插入、删除等。删除后修复实现了两种算法,均比之前的更为简洁。一种是我自己的实现,代码非常简洁,行数更少;一种是Linux、Java等源码版本的实现,实现的略为复杂,但效率更高。两种算法经过测试,在百万级的数据上效率不分伯仲;1000万的数据中,我自己的实现比Linux内核版本的运行时间多2秒左右。

  红黑树的插入相对简单,本文中的代码实现与Linux源码版本也略有差异,效率差别不大。

  其他方法,如查找、遍历等,比较简单,不多做解释。遍历支持前序、中序、后序遍历。

  在研究红黑树的过程中,为了彻底弄懂插入与删除,画了无数的图,以删除修复为例,根据父亲节点、兄弟节点、兄弟节点的左右子节点的空、红、黑的情况,穷举了36中情况,再进行归纳合并。而弄明白以后,发现是如此的简单;实现了多种写法,最后总结为两种最简洁的实现,并进行性能测试。还写了检查一棵树是否是红黑树的代码,将在下篇博客中发布。

  至此,二叉树和红黑树研究完毕。代码如下:

 import 'tree_node.dart';
import 'tree_exception.dart';
import 'traverse_order.dart'; class RBTree<E extends Comparable<E>> {
RBTNode<E> _root;
int _nodeNumbers; RBTree() : _nodeNumbers = 0; factory RBTree.from(Iterable<Comparable<E>> elements) {
var tree = RBTree<E>();
for (var e in elements) tree.insert(e);
return tree;
} bool get isEmpty => _root == null;
int get nodeNumbers => _nodeNumbers;
RBTNode<E> get root => _root; void clear() {
_root = null;
_nodeNumbers = 0;
} bool contains(E value) => find(value) != null; bool delete(E value) => _delete(value, _fixAfterDelete); // the implement in Linux core.
bool quickDelete(E value) => _delete(value, _fixAfterDelete2); RBTNode<E> find(E value) {
var current = _root;
while (current != null) {
var c = value.compareTo(current.value);
if (c == 0) break;
current = c < 0 ? current.left : current.right;
}
return current;
} void insert(E value) {
var inserted = RBTNode<E>(value);
_insert(inserted);
_fixAfterInsert(inserted);
} E get max {
if (isEmpty) throw TreeEmptyException();
var maxNode = _root;
while (maxNode.right != null) maxNode = maxNode.right;
return maxNode.value;
} E get min {
if (isEmpty) throw TreeEmptyException();
return _minNode(_root).value;
} void traverse(void f(E e), [TraverseOrder order = TraverseOrder.inOrder]) =>
_traverse(_root, order, f); void _insert(RBTNode<E> inserted) {
RBTNode<E> p, c = _root;
while (c != null) {
p = c;
c = inserted.value.compareTo(c.value) <= 0 ? c.left : c.right;
} if (p == null) {
_root = inserted;
} else if (inserted.value.compareTo(p.value) <= 0) {
p.left = inserted;
} else {
p.right = inserted;
}
inserted.parent = p;
_nodeNumbers++;
} void _fixAfterInsert(RBTNode<E> node) {
while (_hasRedFather(node) && _hasRedUncle(node)) {
var g = _gparent(node);
g.left.paintBlack();
g.right.paintBlack();
g.paintRed();
node = g;
} if (_hasRedFather(node)) {
var g = _gparent(node);
if (node.parent == g.left) {
if (node == node.parent.right) {
_rotateLeft(node.parent);
node = node.left;
}
_rotateRight(g);
} else {
if (node == node.parent.left) {
_rotateRight(node.parent);
node = node.right;
}
_rotateLeft(g);
}
node.parent.paintBlack();
g.paintRed();
}
_root.paintBlack();
} bool _hasRedFather(RBTNode<E> node) =>
node.parent != null && node.parent.isRed; bool _hasRedUncle(RBTNode<E> node) {
var gparent = _gparent(node);
var uncle = node.parent == gparent.left ? gparent.right : gparent.left;
return uncle != null && uncle.isRed;
} RBTNode _gparent(RBTNode<E> node) => node.parent.parent; bool _delete(E value, void _fix(RBTNode<E> p, bool isLeft)) {
var d = find(value);
if (d == null) return false; if (d.left != null && d.right != null) {
var s = _successor(d);
d.value = s.value;
d = s;
}
var rp = d.left ?? d.right;
rp?.parent = d.parent;
if (d.parent == null)
_root = rp;
else if (d == d.parent.left)
d.parent.left = rp;
else
d.parent.right = rp; if (rp != null)
rp.paintBlack();
else if (d.isBlack && d.parent != null)
_fix(d.parent, d.parent.left == null); _nodeNumbers--;
return true;
} RBTNode<E> _successor(RBTNode<E> d) =>
d.right != null ? _minNode(d.right) : d.left; void _fixAfterDelete(RBTNode<E> p, bool isLeft) {
var c = isLeft ? p.right : p.left;
if (isLeft) {
if (c.isRed) {
p.paintRed();
c.paintBlack();
_rotateLeft(p);
c = p.right;
}
if (c.left != null && c.left.isRed) {
c.left.paint(p.color);
if (p.isRed) p.paintBlack();
_rotateRight(c);
_rotateLeft(p);
} else {
_rotateLeft(p);
if (p.isBlack) {
p.paintRed();
if (c.parent != null) _fixAfterDelete(c.parent, c == c.parent.left);
}
}
} else {
if (c.isRed) {
p.paintRed();
c.paintBlack();
_rotateRight(p);
c = p.left;
}
if (c.right != null && c.right.isRed) {
c.right.paint(p.color);
if (p.isRed) p.paintBlack();
_rotateLeft(c);
_rotateRight(p);
} else {
_rotateRight(p);
if (p.isBlack) {
p.paintRed();
if (c.parent != null) _fixAfterDelete(c.parent, c == c.parent.left);
}
}
}
} // the implement in Linux core.
void _fixAfterDelete2(RBTNode<E> p, bool isLeft) {
var c = isLeft ? p.right : p.left;
if (isLeft) {
if (c.isRed) {
p.paintRed();
c.paintBlack();
_rotateLeft(p);
c = p.right;
}
if ((c.left != null && c.left.isRed) ||
(c.right != null && c.right.isRed)) {
if (c.right == null || c.right.isBlack) {
_rotateRight(c);
c = p.right;
}
c.paint(p.color);
p.paintBlack();
c.right.paintBlack();
_rotateLeft(p);
} else {
c.paintRed();
if (p.isRed)
p.paintBlack();
else if (p.parent != null)
_fixAfterDelete2(p.parent, p == p.parent.left);
}
} else {
if (c.isRed) {
p.paintRed();
c.paintBlack();
_rotateRight(p);
c = p.left;
}
if ((c.left != null && c.left.isRed) ||
(c.right != null && c.right.isRed)) {
if (c.left == null || c.left.isBlack) {
_rotateLeft(c);
c = p.left;
}
c.paint(p.color);
p.paintBlack();
c.left.paintBlack();
_rotateRight(p);
} else {
c.paintRed();
if (p.isRed)
p.paintBlack();
else if (p.parent != null)
_fixAfterDelete2(p.parent, p == p.parent.left);
}
}
} void _rotateLeft(RBTNode<E> node) {
var r = node.right, p = node.parent;
r.parent = p;
if (p == null)
_root = r;
else if (p.left == node)
p.left = r;
else
p.right = r; node.right = r.left;
r.left?.parent = node;
r.left = node;
node.parent = r;
} void _rotateRight(RBTNode<E> node) {
var l = node.left, p = node.parent;
l.parent = p;
if (p == null)
_root = l;
else if (p.left == node)
p.left = l;
else
p.right = l; node.left = l.right;
l.right?.parent = node;
l.right = node;
node.parent = l;
} RBTNode<E> _minNode(RBTNode<E> r) => r.left == null ? r : _minNode(r.left); void _traverse(RBTNode<E> s, TraverseOrder order, void f(E e)) {
if (s == null) return;
switch (order) {
case TraverseOrder.inOrder:
_traverse(s.left, order, f);
f(s.value);
_traverse(s.right, order, f);
break;
case TraverseOrder.preOrder:
f(s.value);
_traverse(s.left, order, f);
_traverse(s.right, order, f);
break;
case TraverseOrder.postOrder:
_traverse(s.left, order, f);
_traverse(s.right, order, f);
f(s.value);
break;
default:
break;
}
}
}

红黑树插入与删除完整代码(dart语言实现)的更多相关文章

  1. stl map底层之红黑树插入步骤详解与代码实现

    转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...

  2. RBtree插入跟删除图解代码

    一.红黑树的简单介绍        RBT 红黑树是一种平衡的二叉查找树.是一种计算机科学中经常使用的数据结构,最典型的应用是实现数据的关联,比如map等数据结构的实现. 红黑树有下面限制: 1. 节 ...

  3. 红黑树插入操作原理及java实现

    红黑树是一种二叉平衡查找树,每个结点上有一个存储位来表示结点的颜色,可以是RED或BLACK.红黑树具有以下性质: (1) 每个结点是红色或是黑色 (2) 根结点是黑色的 (3) 如果一个结点是红色的 ...

  4. 红黑树插入操作---以JDK 源码为例

    红黑树遵循的条件: 1.根节点为黑色. 2.外部节点(叶子节点)为黑色. 3.红色节点的孩子节点为黑色.(由此,红色节点的父节点也必为黑色) 4.从根节点到任一外部节点的路径上,黑节点的数量相同. 节 ...

  5. jdk源码分析红黑树——插入篇

    红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 ...

  6. RedBlack-Tree(红黑树)原理及C++代码实现

    众所周知,红黑树是用途很广的平衡二叉搜索树,用过的都说好.所以我们来看看红黑树的是怎么实现的吧. 红黑树顾名思义,通过红与黑两种颜色来给每个节点上色.其中根结点和叶子结点一定是黑色的,并且红色结点的两 ...

  7. 顺序表的插入和删除(基于c语言)

    插入:在下标p处插入数据x:返回是否成功(0/1) 几个注意点:1.还能否插入数据:2.给的下标p是否是错误的以及p的范围:3.移动时的易错点(从下标大的元素开始):4.n与palist->n; ...

  8. 算法导论学习---红黑树具体解释之插入(C语言实现)

    前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜 ...

  9. TreeMap----的实现原理(红黑树)

    TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致 ...

随机推荐

  1. Windows 常识大全【all】

    解决电脑卡顿问题 电脑常见技巧大全 电脑运行命令CMD集锦 开启Windows 7系统的“上帝模式” Win7下设置护眼的电脑豆沙绿界面 零基础如何组装电脑?装机之家手把手教您电脑组装教程图解 [Ex ...

  2. php linux 环境搭建

    Apache源于NCSAhttpd服务器,经过多次修改,成为世界上最流行的Web服务器软件之一.Apache取自“a patchy server”的读音,意思是充满补丁的服务器,因为它是自由软件,所以 ...

  3. libco协程库上下文切换原理详解

    缘起 libco 协程库在单个线程中实现了多个协程的创建和切换.按照我们通常的编程思路,单个线程中的程序执行流程通常是顺序的,调用函数同样也是 “调用——返回”,每次都是从函数的入口处开始执行.而li ...

  4. BZOJ4766:文艺计算姬(矩阵树定理)

    Description "奋战三星期,造台计算机".小W响应号召,花了三星期造了台文艺计算姬.文艺计算姬比普通计算机有更多的艺术细胞. 普通计算机能计算一个带标号完全图的生成树个数 ...

  5. 【转】网络安全——一图看懂HTTPS建立过程

    阅读目录 准备工作(对应图中prepare1234) 发起链接 最后 关于网络安全加密的介绍可以看之前文章: 1. 网络安全——数据的加密与签名,RSA介绍2. Base64编码.MD5.SHA1-S ...

  6. Scala学习之路 (九)Scala的上界和下届

    一.泛型 1.泛型的介绍 泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性.泛型的典型应用场景是集合及集合中 ...

  7. 利用RMAN恢复整个数据库

    利用RMAN恢复整个数据库案例一 适合场合:恢复的目录一致,同时备份的过程中有归档日志 恢复的数据库目录和down机的数据库一致,还有一个就是RMAN备份的时候已经备份了归档日志. 备份脚本: run ...

  8. Dawn开源项目

    今天本人给大家推荐一个阿里开源的前端构建和工程化工具Dawn. 一. Dawn是什么? Dawn 取「黎明.破晓」之意,原为「阿里云·业务运营团队」内部的前端构建和工程化工具,现已完全开源.它通过 p ...

  9. OpenCV——重映射、仿射变换

    #include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespac ...

  10. JS 点击元素发ajax请求 打开一个新窗口

    JS 点击元素发ajax请求 打开一个新窗口 经常在项目中会碰到这样的需求,点击某个元素后,需要发ajax请求,请求成功以后,开发需要把链接传给前端(或者说请求成功后打开新窗口),前端需要通过新窗口打 ...