红黑树插入与删除完整代码(dart语言实现)
之前分析了红黑树的删除,这里附上红黑树的完整版代码,包括查找、插入、删除等。删除后修复实现了两种算法,均比之前的更为简洁。一种是我自己的实现,代码非常简洁,行数更少;一种是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语言实现)的更多相关文章
- stl map底层之红黑树插入步骤详解与代码实现
转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...
- RBtree插入跟删除图解代码
一.红黑树的简单介绍 RBT 红黑树是一种平衡的二叉查找树.是一种计算机科学中经常使用的数据结构,最典型的应用是实现数据的关联,比如map等数据结构的实现. 红黑树有下面限制: 1. 节 ...
- 红黑树插入操作原理及java实现
红黑树是一种二叉平衡查找树,每个结点上有一个存储位来表示结点的颜色,可以是RED或BLACK.红黑树具有以下性质: (1) 每个结点是红色或是黑色 (2) 根结点是黑色的 (3) 如果一个结点是红色的 ...
- 红黑树插入操作---以JDK 源码为例
红黑树遵循的条件: 1.根节点为黑色. 2.外部节点(叶子节点)为黑色. 3.红色节点的孩子节点为黑色.(由此,红色节点的父节点也必为黑色) 4.从根节点到任一外部节点的路径上,黑节点的数量相同. 节 ...
- jdk源码分析红黑树——插入篇
红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 ...
- RedBlack-Tree(红黑树)原理及C++代码实现
众所周知,红黑树是用途很广的平衡二叉搜索树,用过的都说好.所以我们来看看红黑树的是怎么实现的吧. 红黑树顾名思义,通过红与黑两种颜色来给每个节点上色.其中根结点和叶子结点一定是黑色的,并且红色结点的两 ...
- 顺序表的插入和删除(基于c语言)
插入:在下标p处插入数据x:返回是否成功(0/1) 几个注意点:1.还能否插入数据:2.给的下标p是否是错误的以及p的范围:3.移动时的易错点(从下标大的元素开始):4.n与palist->n; ...
- 算法导论学习---红黑树具体解释之插入(C语言实现)
前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜 ...
- TreeMap----的实现原理(红黑树)
TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致 ...
随机推荐
- 【转】Java学习---10个测试框架介绍
[原文]https://www.toutiao.com/i6594302925458113027/ JAVA 程序员需要用到 10 个测试框架和库 Java 程序员需要用到十大单元测试和自动化集成测试 ...
- python 计时器
今天做自动化界面工具的时候需要用到计时器,查阅了一下,发现以下的这位博友写的很简洁方便且实用 https://blog.csdn.net/qfxx_CSDN/article/details/81412 ...
- 乘风破浪:LeetCode真题_030_Substring with Concatenation of All Words
乘风破浪:LeetCode真题_030_Substring with Concatenation of All Words 一.前言 这次我们还是找字符串的索引,不过,需要将另一个字符串列表中的 ...
- 使用Index()+Match()函数实现更为灵活的VLookUp()
上一篇 http://www.cnblogs.com/-SANG/p/8407017.html 文章中已经介绍了vlookup的用法. 今天要使用index+match实现更为灵活的vlookup 先 ...
- IIS 部署SSL证书
1.导入证书 打开IIS服务管理器,点击计算机名称,双击‘服务器证书 双击打开服务器证书后,点击右则的导入 选择证书文件,点击确定 2.站点开启SSL 选择证书文件,点击确定 点击网站下的站点名称,点 ...
- android开发之一如何升级SDK
看了很多文章,都没有成功,下面这篇才是正解,学海无涯苦作舟. Fetching https://dl-ssl.google.com/android/repository/addons_list-2.x ...
- foreach语句的用法
foreach语句: foreach语句是for语句的特殊简化版本,不能完全取代for语句,但任何foreach语句都可以改写为for语句版本.foreach并不是一个关键字,习惯上将这种特殊的for ...
- T4学习- 1、简介
一.T4简介 T4(Text Template Transformation Toolkit)在 Visual Studio 中,"T4 文本模板"是由一些文本块和控制 ...
- 常用npm 命令
npm 官方网站:npm的使用说明 安装模块 npm install 安装当前目录package.json文件中配置的dependencies模块 安装本地的模块文件 npm install ...
- 使用docker搭建hadoop环境,并配置伪分布式模式
docker 1.下载docker镜像 docker pull registry.cn-hangzhou.aliyuncs.com/kaibb/hadoop:latest 注:此镜像为阿里云个人上传镜 ...