数据结构系列之2-3-4树的插入、查找、删除和遍历完整版源代码实现与分析(dart语言实现)
本文属于原创,转载请注明来源。
在上一篇博文中,详细介绍了2-3树的操作(具体地址:https://www.cnblogs.com/outerspace/p/10861488.html),那么对于更多教科书上更为普遍的2-3-4树,在这里也给出 树的定义、节点的定义、插入、查找、删除和遍历等操作的源代码实现。
关于2-3-4树的文字定义,网上很多,可自行百度,这里不再重复论述。但看了很多博文,关于插入等操作的实现较多,基本上没有实现删除操作的。因此本博文给出完整版的2-3-4树的插入、删除、查找及遍历等源代码。
另,2-3-4树代码的实现,与2-3树非常类似,故关于代码的分析,请参考2-3树(https://www.cnblogs.com/outerspace/p/10861488.html)的介绍,这里仅给出简要说明。
如果节点允许的最大元素数量超过3个,非叶子节点最大孩子数量超过4个,就成了多叉树,后续有机会分享一下一个关于8叉树的实现。
本代码由dart语言实现,关于dart,一门类似Java、JavaScript的语言,也是现在比较流行的flutter的后台语言,有兴趣的同学可以去dart官网了解:
https://dart.dev
闲话少说,上代码。
// 树的定义 2-3-4树
class QuaternaryTree<E extends Comparable<E>> {
QuaterNode<E> _root;
int _elementsCount; factory QuaternaryTree.of(Iterable<Comparable<E>> elements) {
var tree = QuaternaryTree<E>();
for (var e in elements) tree.insert(e);
return tree;
} QuaternaryTree() : _elementsCount = 0; int get elementsCount => _elementsCount;
QuaterNode<E> get root => _root; // 计算树高
int get height {
var h = 0, c = root;
while (c != null) {
h++;
c = c.isNotLeaf ? c.branches[0] : null;
}
return h;
} bool get isEmpty => _root == null; bool contains(E value) => find(value) != null; // 查找树中是否存在某个值
// 若找到,返回包含此值的树节点,否则返回null
QuaterNode<E> find(E value) {
var c = root;
while (c != null) {
var i = 0;
while (i < c.size && c.items[i].compareTo(value) < 0) i++;
if (i < c.size && c.items[i] == value) break;
c = c.isNotLeaf ? c.branches[i] : null;
}
return c;
} // 插入新的值,如果值已经存在,则不做任何操作
// 插入始终在叶子节点进行
void insert(E value) {
var c = root, i = 0;
while (c != null) {
i = 0;
while (i < c.size && c.items[i].compareTo(value) < 0) i++;
if (i < c.size && c.items[i] == value) return;
if (c.isLeaf) break;
c = c.branches[i];
}
if (c != null) {
c.items.insert(i, value); // 判断插入后是否需要修复
if (c.isOverflow) _fixAfterIns(c);
} else {
_root = QuaterNode([value]);
}
_elementsCount++;
} // 删除指定值
bool delete(E value) {
// 首先查找该值
var d = find(value);
// 若不存在,直接返回
if (d == null) return false; // 查找该值在节点中的索引顺序
var i = d.find(value); // 如果节点不是叶子节点,则用后继节点的值替代该值
// 这样删除操作将转移为删除叶子节点的值
if (d.isNotLeaf) {
var s = _successor(d.branches[i + 1]);
d.items[i] = s.items[0];
d = s;
i = 0;
}
d.items.removeAt(i);
_elementsCount--; // 根据2-3-4树的定义,节点不能为空,因此判断删除后是否需要修复
if (d.items.isEmpty) _fixAfterDel(d);
return true;
} // 遍历树
void traverse(void func(List<E> items)) {
if (!isEmpty) _traverse(_root, func);
} // 插入修复
// 注意,插入修复时,采用节点分裂的形式进行修复;
// 分裂出来的新的父节点需要被上层节点吸收(如果存在上层节点的话)
void _fixAfterIns(QuaterNode<E> c) {
while (c != null && c.isOverflow) {
var t = _split(c);
c = t.parent != null ? _absorb(t) : null;
}
} // 节点分裂,由于分裂->吸收,可能会递归分裂;
// 因此需要注意根节点的更新判断以及子节点的处理;
QuaterNode<E> _split(QuaterNode<E> c) {
var mid = c.size ~/ 2,
l = QuaterNode._internal(c.items.sublist(0, mid)),
nc = QuaterNode._internal(c.items.sublist(mid, mid + 1)),
r = QuaterNode._internal(c.items.sublist(mid + 1));
nc.branches.addAll([l, r]);
l.parent = r.parent = nc; nc.parent = c.parent;
if (c.parent != null) {
var i = 0;
while (c.parent.branches[i] != c) i++;
c.parent.branches[i] = nc;
} else {
_root = nc;
}
if (c.isNotLeaf) {
l.branches
..addAll(c.branches.getRange(0, mid + 1))
..forEach((b) => b.parent = l);
r.branches
..addAll(c.branches.getRange(mid + 1, c.branches.length))
..forEach((b) => b.parent = r);
}
return nc;
} // 上层节点吸收新分裂出来的节点,以保持树的平衡
QuaterNode<E> _absorb(QuaterNode<E> c) {
var i = 0, p = c.parent;
while (p.branches[i] != c) i++;
p.items.insertAll(i, c.items);
p.branches.replaceRange(i, i + 1, c.branches);
c.branches.forEach((b) => b.parent = p);
return p;
} // 查找后继节点
QuaterNode<E> _successor(QuaterNode<E> p) {
while (p.isNotLeaf) p = p.branches[0];
return p;
} // 删除修复
void _fixAfterDel(QuaterNode<E> d) {
if (d == root) {
_root = null;
} else {
var ct = 0;
while (d.size < (1 << ct + 1) - 1 && d.parent != null) {
// 塌缩父节点
_collapse(d.parent);
d = d.parent;
ct++;
} // 如果塌缩到了树的根,则树高减1
// if (d.size < (1 << ct + 1) - 1) ct--;
if (d == root) ct--; // 修剪多余的值
var rest = _prune(d, (1 << ct + 1) - 1); // 重新展开
_expand(d, ct); // 将刚才修剪掉的多余的值重新插入树
for (var e in rest) insert(e);
}
} // 树的塌缩函数,注意递归塌缩
void _collapse(QuaterNode<E> p) {
if (p.isLeaf) return;
for (var i = p.branches.length - 1; i >= 0; i--) {
_collapse(p.branches[i]);
p.items.insertAll(i, p.branches[i].items);
}
p.branches.clear();
} // 修剪,保留满足展开为满二叉树的最小数量的值
List<E> _prune(QuaterNode<E> d, int least) {
var t = d.size ~/ least, rest = <E>[];
if (t < 2) {
rest.addAll(d.items.getRange(least, d.size));
d.items.removeRange(least, d.size);
} else {
// 跳跃修剪,以保证二次插入时分裂的次数较少
var list = <E>[];
for (var i = 0; i < d.size; i++) {
if (i % t == 0 && list.length < least)
list.add(d.items[i]);
else
rest.add(d.items[i]);
}
d.items = list;
}
_elementsCount -= rest.length;
return rest;
} // 递归展开修剪后的节点,ct代表展开的层数或高度
void _expand(QuaterNode<E> p, int ct) {
if (ct == 0) return;
p = _split(p);
for (var b in p.branches) _expand(b, ct - 1);
} void _traverse(QuaterNode<E> r, void f(List<E> items)) {
f(r.items);
for (var b in r.branches) _traverse(b, f);
}
} class QuaterNode<E extends Comparable<E>> {
static final int capacity = 3;
List<E> items;
List<QuaterNode<E>> branches;
QuaterNode<E> parent; factory QuaterNode(List<E> elements) {
if (elements.length > capacity) throw StateError('too many elements.');
return QuaterNode._internal(elements);
} QuaterNode._internal(List<E> elements)
: items = [],
branches = [] {
items.addAll(elements);
} int get size => items.length;
bool get isOverflow => size > capacity;
bool get isLeaf => branches.isEmpty;
bool get isNotLeaf => !isLeaf; bool contains(E value) => items.contains(value);
int find(E value) => items.indexOf(value); String toString() => items.toString();
}
-end-
数据结构系列之2-3-4树的插入、查找、删除和遍历完整版源代码实现与分析(dart语言实现)的更多相关文章
- 数据结构系列之2-3树的插入、查找、删除和遍历完整版代码实现(dart语言实现)
弄懂了二叉树以后,再来看2-3树.网上.书上看了一堆文章和讲解,大部分是概念,很少有代码实现,尤其是删除操作的代码实现.当然,因为2-3树的特性,插入和删除都是比较复杂的,因此经过思考,独创了删除时分 ...
- B树和B+树的插入、删除图文详解
简介:本文主要介绍了B树和B+树的插入.删除操作.写这篇博客的目的是发现没有相关博客以举例的方式详细介绍B+树的相关操作,由于自身对某些细节也感到很迷惑,通过查阅相关资料,对B+树的操作有所顿悟,写下 ...
- B树和B+树的插入、删除图文详解(good)
B树和B+树的插入.删除图文详解 1. B树 1. B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数 ...
- AVL树的插入与删除
AVL 树要在插入和删除结点后保持平衡,旋转操作必不可少.关键是理解什么时候应该左旋.右旋和双旋.在Youtube上看到一位老师的视频对这个概念讲解得非常清楚,再结合算法书和网络的博文,记录如下. 1 ...
- AVL 树的插入、删除、旋转归纳
参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339 1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根 ...
- B+树的插入、删除(附源代码)
B+ Tree Index B+树的插入 B+树的删除 完整测试代码 Basic B+树和B树类似(有关B树:http://www.cnblogs.com/YuNanlong/p/6354029.ht ...
- [数据结构与算法分析(Mark Allen Weiss)]二叉树的插入与删除 @ Python
二叉树的插入与删除,来自Mark Allen Weiss的<数据结构与算法分析>. # Definition for a binary tree node class TreeNode: ...
- MySQL B+树 的插入与删除
一.MySQL Index 的插入 有如下B+树,其高度为2,每页可存放4条记录,扇出为5.所有记录都在叶子节点上, 并且是顺序存放,如果用户从最左边的叶子节点开始顺序遍历,可以得到所有简直的顺序 排 ...
- 转:B树和B+树的插入、删除图文详解
如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. B树 1. B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了 ...
随机推荐
- [工具] BurpSuite--Intruder功能
BurpSuite--Intruder功能 0x00 配置说明 intruder是进行爆破的,基本流程是标注请求的爆破参数,然后配置字段,选择爆破方式进行爆破,下面来记录下工具的使用 选中intrud ...
- [POJ1664]放苹果(动态规划)
[POJ1664]放苹果 Description 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. Input 第 ...
- CodeForces - 841D Leha and another game about graph
给出一个连通图,并给每个点赋一个d值0或1或-1,要求选出一个边的集合,使得所有的点i要么d[i] == -1,要么 dgree[i] % 2 == d[i],dgree[i]代表i结点的度数. 考虑 ...
- Android开发艺术探索笔记之Activity
内容来源:Android开发艺术探索第一章:Activity的生命周期与启动模式 不能在onPause中做重量级的操作,因为必须执行完成以后新Activity才能Resume.onPause和onSt ...
- 【模板】Lucas定理
代码如下 #include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=1e5+10 ...
- spark读取kafka数据 createStream和createDirectStream的区别
1.KafkaUtils.createDstream 构造函数为KafkaUtils.createDstream(ssc, [zk], [consumer group id], [per-topic, ...
- 【leetcode】Reach a Number
题目: You are standing at position 0 on an infinite number line. There is a goal at position target. O ...
- Qt第三方库----QCustomPlot
一.软件下载 下载地址:http://www.qcustomplot.com/index.php/download 这里推荐下载第一个链接的内容: 注:这里的第三方库要放在非中文目录下. 二.配置 ( ...
- javac不是内部或外部命令在win10上的解决方案
Path环境变量能够让你在任何路径都能使用命令,可能你百度谷歌了各种方案都无法解决javac无法使用的问题,那么你可以试试如下解决方案: 首先博主配置了JAVA_HOME 参数为 C:\Program ...
- event.stopPropagation()和event.preventDefault(),return false的区别
我写公司的官网遇到一个问题,轮播图的上一层有一块内容,用鼠标拖动那块内容的时候下一层的轮播图也会跟着拖动,而上面的那层的内容是不会动的,我想这就是冒泡事件在作祟了吧 跟冒泡事件相关的,我想到三个: 1 ...