B树
B树介绍
B树是平衡多叉树,是平衡二叉搜索树的一般化(图来自网络)
B树的用途
B树通过增加元素和节点的方式实现完美平衡搜索树,避免平衡二叉树多次旋转导致的时间性能损失,即用空间换时间
插入通式(看不懂先看下面的图)
m阶B树插入一个元素时,首先判断元素是否存在,如果不存在,即在叶子结点处结束,判断叶节点
- 若该节点元素个数小于m-1,直接插入
- 若该节点元素个数等于m-1,引起节点分裂,取中间元素(偶数则随机选取)插入到父节点中
- 重复上面动作,直到所有节点符合B树的规则,若一直分裂到根节点,则生成新的根节点,高度增加1
删除通式
查找待删除元素是否存在,判断待删除元素是叶节点元素还是非叶节点元素
- 删除非叶节点元素,则用后继元素替代待删除元素在节点中的位置,问题转为了删除后继元素,以此循环,直到变成删除叶节点元素
- 删除叶节点元素,判断元素数量,若不小于⌈m/2⌉-1,则直接删除,否则判断兄弟结点元素数量
- 若兄弟节点元素数量不小于⌈m/2⌉-1,则父结点元素下移,兄弟结点元素上移
- 若兄弟节点元素数量等于⌈m/2⌉-1,则父节点和兄弟结点合并成一个结点,循环判断父节点的兄弟节点,若合并到根节点则高度减1
3阶B树(2-3树)
3阶B树每个节点最多有3个子节点,2个元素,一颗2-3树或为一颗空树,或有以下节点组成:
-
2-节点:含一个元素和两个子树(左右子树),左子树所有元素的值均小于它父节点,右子树所有元素的值均大于它父节点
-
3-节点:含两个元素和三个子树(左中右子树),左子树所有元素的值均小于它父节点,中子树所有元素的值都位于父节点两个元素之间,右子树所有元素的值均大于它父节点
插入
向2-节点插入,则将2-节点变为3-节点(即待插入节点不满直接插入)
向3节点插入(即待插入节点已满),则分为3种情况
- 向一棵只含有3-结点的树插入,中间值成为根结点,左右值成为左右节点,树高+1
- 向父结点为2-结点的3-结点中插入,待插入节点成为4-结点,其中间值升到父节点,使父节点成为一个3-节点,然后将其左右值分别挂在这个父节点的恰当位置,树的高度不发生改变
- 向父节点为3-结点的3-结点中插入,待插入节点成为4-结点,其中间值升到父节点,然后父节点成为4-结点,其中间值升到父节点…这是一个递归的过程,直到遇到的结点不再是3-结点
搜索
搜索和二叉搜索树一样,小于往左走,大于往右走,位于中间往中间走
删除
- 关于2-3树的删除,很多都讲的不对,本文参考了这个链接 https://studygolang.com/articles/27787?fr=sidebar ,并对其进行了总结(如果看不懂我的,可以看看他的)
对于2-3树的删除,分为删除非叶节点元素和删除叶节点元素
- 删除非叶节点元素:元素可能位于2-节点或3节点(必有后继节点),从子树中找到其后继节点元素1(必为叶节点)替代,问题转化为删除叶节点元素1
待删除元素所在位置 | 后继节点 |
---|---|
2-节点元素1 | 其右子树最小节点 |
3-节点元素1 | 其中子树最小节点 |
3-节点元素2 | 其右子树最小节点 |
- 删除叶节点元素,若叶节点已满则直接删除,不影响平衡
- 删除叶节点元素,若未满则需要考虑其父节点,若父节点为2节点,递归 / 循环判断兄弟节点,若兄弟节点满,则重新分配,若兄弟节点未满则合并,若合并到根节点则树高减一(完全二叉树)
(删除左右两边都是类似的,主要是合并或分配的方向和元素不同,注意合并会拐弯)
- 删除叶节点元素,若未满则需要考虑其父节点,若父节点为3节点,则判断删除的是哪个子树,下图删除左未满叶节点(调整可以有很多方式,只要平衡就行,如下不改变右节点)
下图删除中未满叶节点(调整可以有很多方式,只要平衡就行,删除中节点可能要改变左右节点)
下图删除右未满叶节点(调整可以有很多方式,只要平衡就行,如下不改变左节点)
至此2-3树的删除就完了
代码实现
- 节点数据类Node的构造函数分别生成叶节点和非叶节点用于替换原来的节点
- toString()、innerMidOrderTraversal()、innerLevelTraversal() 用于打印中序、层次遍历验证结果
- getParent() 获取父类,通过比较元素大小判断(这里可能写的不太好)
- searchNode() 用于找到待插入/删除的节点
- search() 查找元素,找到待插入位置判断元素是否已经存在
- replaceNode() 把增加删除看作替换节点,因为java是值传递,替换节点需要重新链接,故替换实际是替换内部属性,和Node构造函数结合避免了大量的属性设置代码,增加代码可读性
- isFull()、isLeaf()、isTwoNode()、isThreeNode() 判断是否已满、叶节点、2-节点、3-节点
add()、spilt()、splitLeaf()、spiltTwoNode()、spiltThreeNode() 为添加元素代码,具体为
- 第一次插入元素为根节点
- searchNode()找到待插入的叶节点,判断是否已满,未满直接插入,已满则需要分裂
- spilt()分裂先分裂叶节点,若父节点为3-节点则循环分裂,若父节点为2-节点则分裂为3-节点
- 具体的分裂步骤需结合图和代码理解
del()、getMinNode()、delInnerNode()、delLeaf()、delFullLeaf()、delNotFullLeaf()、parentIsTwoNode()、parentIsThreeNode()为删除元素代码,具体为
- del() 判断是删除叶节点元素还是非叶节点元素
- delInnerNode() 将删除非叶节点元素转化为删除叶节点元素
- delLeaf() 判断待删除叶节点是否已满,满直接删除,未满则需调整
- delNotFullLeaf() 判断未满待删除叶节点其父节点是2-节点还是3-节点
- 具体合并和调整步骤需结合图和代码理解
class Tree23 {
private class Node {
int value1;
int value2;
Node leftChild;
Node midChild;
Node rightChild;
//生成1元素叶节点
public Node(int value1) {
this.value1 = value1;
}
//生成2元素叶节点
public Node(int value1, int value2) {
this.value1 = value1;
this.value2 = value2;
}
//生成2-非叶节点
public Node(int value1, Node leftChild, Node rightChild) {
this.value1 = value1;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
//生成3-非叶节点
public Node(int value1, int value2, Node leftChild, Node midChild, Node rightChild) {
this.value1 = value1;
this.value2 = value2;
this.leftChild = leftChild;
this.midChild = midChild;
this.rightChild = rightChild;
}
}
private Node root;
@NonNull
@Override
public String toString() {
System.out.print("\n对节点层次遍历:");
innerLevelTraversal();
System.out.println();
System.out.print("对元素中序遍历: ");
innerMidOrderTraversal(root);
System.out.println();
System.out.println("----------------------------------");
return "";
}
private void innerMidOrderTraversal(Node node) {
if (node == null) {
return;
}
innerMidOrderTraversal(node.leftChild);
System.out.print(node.value1 + " ");
innerMidOrderTraversal(node.midChild);
if (node.value2 != 0) {
System.out.print(node.value2 + " ");
}
innerMidOrderTraversal(node.rightChild);
}
private void innerLevelTraversal() {
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
System.out.print("[value1=" + node.value1 + ",value2=" + node.value2 + "] ");
if (node.leftChild != null) {
queue.offer(node.leftChild);
}
if (node.midChild != null) {
queue.offer(node.midChild);
}
if (node.rightChild != null) {
queue.offer(node.rightChild);
}
}
}
private Node getParent(Node node) { //通过比较元素判断往哪走,这里写的还待优化
if (root == node) {
return null;
}
if (node == null) {
return null;
}
Node current = root;
Node parent = null;
while (current != null) {
if (!isFull(node) && !isFull(current)) {
if (node.value1 < current.value1) {
parent = current;
current = current.leftChild;
} else if (node.value1 > current.value1) {
parent = current;
current = current.rightChild;
} else {
return parent;
}
} else if (isFull(node) && !isFull(current)) {
if (node.value2 < current.value1) {
parent = current;
current = current.leftChild;
} else if (node.value1 > current.value1) {
parent = current;
current = current.rightChild;
} else {
return parent;
}
} else if (!isFull(node) && isFull(current)) {
if (node.value1 < current.value1) {
parent = current;
current = current.leftChild;
} else if (node.value1 > current.value1 && node.value1 < current.value2) {
parent = current;
current = current.midChild;
} else if (node.value1 > current.value2) {
parent = current;
current = current.rightChild;
} else {
return parent;
}
} else if (isFull(node) && isFull(current)) {
if (node.value2 < current.value1) {
parent = current;
current = current.leftChild;
} else if (node.value1 > current.value1 && node.value2 < current.value2) {
parent = current;
current = current.midChild;
} else if (node.value1 > current.value2) {
parent = current;
current = current.rightChild;
} else {
return parent;
}
}
}
return parent;
}
private Node searchNode(int value) {
Node insertNode = root;
while (!isLeaf(insertNode)) { //找到元素待插入或待删除的叶节点,未处理相等元素插入的情况
if (value == insertNode.value1 || value == insertNode.value2) {
return insertNode;
} else if (value < insertNode.value1) {
insertNode = insertNode.leftChild;
} else if (value > insertNode.value1 && value < insertNode.value2) {
insertNode = insertNode.midChild;
} else if (value > insertNode.value2) {
insertNode = insertNode.rightChild;
}
}
return insertNode;
}
public boolean search(int value) { //找到待插入的节点判断元素是否存在
Node insertNode = searchNode(value);
return insertNode.value1 == value || insertNode.value2 == value;
}
private void replaceNode(Node source, Node target) { //统一替换元素各个属性,避免多处设置影响可读性
if (source == null || target == null) {
return;
}
source.value1 = target.value1;
source.value2 = target.value2;
source.leftChild = target.leftChild;
source.midChild = target.midChild;
source.rightChild = target.rightChild;
}
private boolean isFull(Node node) { //判断元素是否已满
if (node == null) {
return false;
}
return node.value1 != 0 && node.value2 != 0;
}
private boolean isLeaf(Node node) { //判断是否是叶节点
if (node == null) {
return false;
}
return node.leftChild == null && node.midChild == null && node.rightChild == null;
}
private boolean isTwoNode(Node node) { //判断是否有左右节点
if (node == null) {
return false;
}
return node.leftChild != null && node.midChild == null && node.rightChild != null;
}
private boolean isThreeNode(Node node) { //判断是否有左中右节点
if (node == null) {
return false;
}
return node.leftChild != null && node.midChild != null && node.rightChild != null;
}
public void add(int value) {
System.out.print("插入:" + value + " ");
if (root == null) { //若第一次插入则成为根节点
root = new Node(value);
return;
}
Node insertNode = searchNode(value); //找到待插入的节点,一定是叶节点
if (isFull(insertNode)) { //待插入节点已满,需要分裂
spilt(value, insertNode);
} else { //待插入的节点未满,可直接插入、检查是否需要交换元素位置
if (value > insertNode.value1) {
insertNode.value2 = value;
} else {
insertNode.value2 = insertNode.value1;
insertNode.value1 = value;
}
}
}
private void spilt(int value, Node insertNode) {
int up = splitLeaf(value, insertNode); //分裂叶节点
Node insertNodeParent = getParent(insertNode);
if (insertNodeParent != null) { //若存在父节点则分裂父节点
while (isThreeNode(insertNodeParent)) { //若父节点为3节点则递归分裂3节点,让3节点变成2节点,若递归到根节点树高+1
up = spiltThreeNode(up, insertNodeParent);
insertNodeParent = getParent(insertNodeParent);
}
if (isTwoNode(insertNodeParent)) { //父节点为2节点(或递归到2节点)则让2节点变成3节点
spiltTwoNode(up, insertNodeParent);
}
}
}
private int splitLeaf(int value, Node insertNode) { //分裂叶节点,中间值成为根节点,左右值成为左右子树
Node replaceNode = null;
if (value < insertNode.value1) {
replaceNode = new Node(insertNode.value1, new Node(value), new Node(insertNode.value2));
} else if (value > insertNode.value1 && value < insertNode.value2) {
replaceNode = new Node(value, new Node(insertNode.value1), new Node(insertNode.value2));
} else if (value > insertNode.value2) {
replaceNode = new Node(insertNode.value2, new Node(insertNode.value1), new Node(value));
}
replaceNode(insertNode, replaceNode);
return insertNode.value1;
}
private void spiltTwoNode(int value, Node insertNode) { //分裂2节点成3节点
Node replaceNode = null;
if (value < insertNode.value1) { //若上升的值是左子树的根节点,则一定小于父节点,让其成为value1,将其左右子树变成父节点的左中子树
replaceNode = new Node(value, insertNode.value1, insertNode.leftChild.leftChild, insertNode.leftChild.rightChild, insertNode.rightChild);
} else if (value > insertNode.value1) { //若上升的值是右子树的根节点,则一定大于父节点,让其成为value2,将其左右子树变成父节点的中右子树
replaceNode = new Node(insertNode.value1, value, insertNode.leftChild, insertNode.rightChild.leftChild, insertNode.rightChild.rightChild);
}
replaceNode(insertNode, replaceNode);
}
private int spiltThreeNode(int value, Node insertNode) { //分裂3节点成2节点,树高+1
Node replaceNode = null;
Node left = null;
Node right = null;
if (value < insertNode.value1) { //中间值成为新的父节点,值从左子树上升,说明左子树已经分裂好了,右节点拿到父节点的中右子树
right = new Node(insertNode.value2, insertNode.midChild, insertNode.rightChild);
replaceNode = new Node(insertNode.value1, insertNode.leftChild, right);
} else if (value > insertNode.value1 && value < insertNode.value2) { //中间值成为新的父节点,值从中子树上升,左节点拿到父节点左子树+中子树的左节点,右节点拿到父节点中子树的右节点+右子树
left = new Node(insertNode.value1, insertNode.leftChild, insertNode.midChild.leftChild);
right = new Node(insertNode.value2, insertNode.midChild.rightChild, insertNode.rightChild);
replaceNode = new Node(value, left, right);
} else if (value > insertNode.value2) { //中间值成为新的父节点,值从右子树上升,说明右子树已经分裂好了,左节点拿到父节点的左中子树
left = new Node(insertNode.value1, insertNode.leftChild, insertNode.midChild);
replaceNode = new Node(insertNode.value2, left, insertNode.rightChild);
}
replaceNode(insertNode, replaceNode);
return insertNode.value1; //返回上升的节点,即根节点
}
public void del(int value) {
System.out.print("删除:" + value + " ");
Node removeNode = searchNode(value);
if (isLeaf(removeNode)) { //删除叶节点
delLeaf(value, removeNode, getParent(removeNode));
} else {
delInnerNode(value, removeNode); //删除非叶节点
}
}
private Node getMinNode(Node node) { //获取子树中最小节点
while (node.leftChild != null) {
node = node.leftChild;
}
return node;
}
private void delInnerNode(int value, Node removeNode) { //删除非叶节点元素,用后继节点元素value1替换
Node sucNode = null;
if (isTwoNode(removeNode)) { //若待删除元素在2节点,后继节点为右子树最小叶节点
sucNode = getMinNode(removeNode.rightChild);
removeNode.value1 = sucNode.value1;
} else if (isThreeNode(removeNode)) {
if (value == removeNode.value1) { //若待删除元素在3节点的value1,后继节点为中子树最小叶节点
sucNode = getMinNode(removeNode.midChild);
removeNode.value1 = sucNode.value1;
} else if (value == removeNode.value2) { //若待删除元素在3节点的value2,后继节点为右子树最小叶节点
sucNode = getMinNode(removeNode.rightChild);
removeNode.value2 = sucNode.value1;
}
}
delLeaf(sucNode.value1, sucNode, removeNode); //问题转为删除叶节点value1
}
private void delLeaf(int value, Node removeNode, Node removeNodeParent) {
if (isFull(removeNode)) { //删除已满叶节点,判断删除的是value1还是value2
delFullLeaf(value, removeNode);
} else { //删除未满未满叶节点,删除后需要调整,调整过程和删除的value无关
delNotFullLeaf(removeNode, removeNodeParent);
}
}
private void delFullLeaf(int value, Node removeNode) {
if (value == removeNode.value1) { //若删除value1,则先用value2覆盖,再置空value2
removeNode.value1 = removeNode.value2;
}
removeNode.value2 = 0; //删除value2直接置空
}
private void delNotFullLeaf(Node removeNode, Node removeNodeParent) { //删除未满叶节点需要考虑其父节点
if (isTwoNode(removeNodeParent)) {
parentIsTwoNode(removeNode, removeNodeParent);
} else if (isThreeNode(removeNodeParent)) {
parentIsThreeNode(removeNode, removeNodeParent);
}
}
private void parentIsTwoNode(Node removeNode, Node removeNodeParent) { //当父节点为2节点,需要考虑完全二叉树的情况,这里比较复杂,最好结合图理解
Node mergeNode = null;
while (removeNodeParent != null) { //若父节点不为空,则循环判断兄弟节点
if (removeNode == removeNodeParent.leftChild) {
if (!isFull(removeNodeParent.rightChild)) { //删除的是左节点且右节点未满,则合并右父节点
mergeNode = new Node(removeNodeParent.value1, removeNodeParent.rightChild.value1, mergeNode, removeNodeParent.rightChild.leftChild, removeNodeParent.rightChild.rightChild);
} else { //删除的是左节点且右节点已满,则让右节点从3节点分裂成2节点
Node left = new Node(removeNodeParent.value1, mergeNode, removeNodeParent.rightChild.leftChild);
Node right = new Node(removeNodeParent.rightChild.value2, removeNodeParent.rightChild.midChild, removeNodeParent.rightChild.rightChild);
Node replace = new Node(removeNodeParent.rightChild.value1, left, right);
replaceNode(removeNodeParent, replace);
break;
}
} else if (removeNode == removeNodeParent.rightChild) {
if (!isFull(removeNodeParent.leftChild)) { //删除的是右节点且左未满,则合并左父节点
mergeNode = new Node(removeNodeParent.leftChild.value1, removeNodeParent.value1, removeNodeParent.leftChild.leftChild, removeNodeParent.leftChild.rightChild, mergeNode);
} else { //删除的是右节点且左节点已满,则让左节点从3节点分裂成2节点
Node left = new Node(removeNodeParent.leftChild.value1, removeNodeParent.leftChild.leftChild, removeNodeParent.leftChild.midChild);
Node right = new Node(removeNodeParent.value1, removeNodeParent.leftChild.rightChild, mergeNode);
Node replace = new Node(removeNodeParent.leftChild.value2, left, right);
replaceNode(removeNodeParent, replace);
break;
}
}
removeNode = removeNodeParent; //赋值,循环判断下一个是左还是右节点
removeNodeParent = getParent(removeNodeParent);
}
if (removeNodeParent == null) { //若合并到了根节点(完全二叉树),替换根节点,树高-1
replaceNode(root, mergeNode);
}
}
private void parentIsThreeNode(Node removeNode, Node removeNodeParent) { //当父节点为3节点,可能变成3节点或2节点
Node replaceNode;
if (removeNode == removeNodeParent.leftChild) {
if (isFull(removeNodeParent.midChild)) { //若删除左节点且中节点已满,调整3节点
Node left = new Node(removeNodeParent.value1);
Node mid = new Node(removeNodeParent.midChild.value2);
replaceNode = new Node(removeNodeParent.midChild.value1, removeNodeParent.value2, left, mid, removeNodeParent.rightChild);
replaceNode(removeNodeParent, replaceNode);
} else { //若删除左节点且中节点未满,变为2节点
Node left = new Node(removeNodeParent.value1, removeNodeParent.midChild.value1);
replaceNode = new Node(removeNodeParent.value2, left, removeNodeParent.rightChild);
replaceNode(removeNodeParent, replaceNode);
}
} else if (removeNode == removeNodeParent.midChild) {
if (!isFull(removeNodeParent.leftChild)) { //若删除中节点且左节点未满,变为2节点
Node left = new Node(removeNodeParent.leftChild.value1, removeNodeParent.value1);
replaceNode = new Node(removeNodeParent.value2, left, removeNodeParent.rightChild);
replaceNode(removeNodeParent, replaceNode);
} else if (!isFull(removeNodeParent.rightChild)) { //若删除中节点且左节点满右节点未满,变为2节点
Node right = new Node(removeNodeParent.value2, removeNodeParent.rightChild.value1);
replaceNode = new Node(removeNodeParent.value1, removeNodeParent.leftChild, right);
replaceNode(removeNodeParent, replaceNode);
} else { //若删除中节点且左右节点都满,调整3节点
Node mid = new Node(removeNodeParent.value2);
Node right = new Node(removeNodeParent.rightChild.value2);
replaceNode = new Node(removeNodeParent.value1, removeNodeParent.rightChild.value1, removeNodeParent.leftChild, mid, right);
replaceNode(removeNodeParent, replaceNode);
}
} else if (removeNode == removeNodeParent.rightChild) {
if (isFull(removeNodeParent.midChild)) { //若删除右节点且中节点满,调整3节点
Node mid = new Node(removeNodeParent.midChild.value1);
Node right = new Node(removeNodeParent.value2);
replaceNode = new Node(removeNodeParent.value1, removeNodeParent.midChild.value2, removeNodeParent.leftChild, mid, right);
replaceNode(removeNodeParent, replaceNode);
} else { //若删除右节点且中节点未满,变为2节点
Node right = new Node(removeNodeParent.midChild.value1, removeNodeParent.value2);
replaceNode = new Node(removeNodeParent.value1, removeNodeParent.leftChild, right);
replaceNode(removeNodeParent, replaceNode);
}
}
}
}
测试
插入测试
向2节点插入变为3节点
向 [50, 0] 插入30变为 [30, 50]
Tree23 tree = new Tree23();
tree.add(50);
System.out.println(tree);
tree.add(30);
System.out.println(tree);
打印如下
插入:50
对节点层次遍历:[value1=50,value2=0]
对元素中序遍历: 50
----------------------------------
插入:30
对节点层次遍历:[value1=30,value2=50]
对元素中序遍历: 30 50
----------------------------------
向只含3节点的树插入
向 [30, 50] 插入80 分裂为2节点,[50, 0] 为根,[30, 0] 和 [80, 0] 为左右子树
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
System.out.println(tree);
tree.add(80);
System.out.println(tree);
打印如下
插入:50 插入:30
对节点层次遍历:[value1=30,value2=50]
对元素中序遍历: 30 50
----------------------------------
插入:80
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0]
对元素中序遍历: 30 50 80
----------------------------------
向父结点为2-结点的3-结点中插入
向父节点为 [50, 0] 的右子节点 [80, 90] 插入 70,父节点分裂为3节点
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.add(70);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90]
对元素中序遍历: 30 50 80 90
----------------------------------
插入:70
对节点层次遍历:[value1=50,value2=80] [value1=30,value2=0] [value1=70,value2=0] [value1=90,value2=0]
对元素中序遍历: 30 50 70 80 90
----------------------------------
向父节点为3-结点的3-结点中插入
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(70);
tree.add(20);
tree.add(40);
tree.add(10);
tree.add(25);
tree.add(15);
System.out.println(tree);
tree.add(18);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:70 插入:20 插入:40 插入:10 插入:25 插入:15
对节点层次遍历:[value1=50,value2=0] [value1=20,value2=30] [value1=80,value2=0] [value1=10,value2=15] [value1=25,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0]
对元素中序遍历: 10 15 20 25 30 40 50 70 80 90
----------------------------------
插入:18
对节点层次遍历:[value1=20,value2=50] [value1=15,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=10,value2=0] [value1=18,value2=0] [value1=25,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0]
对元素中序遍历: 10 15 18 20 25 30 40 50 70 80 90
----------------------------------
搜索测试
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
boolean search = tree.Search(80);
if(search){
System.out.println("found");
}else {
System.out.println("not found");
}
打印如下
插入:50 插入:30 插入:80 插入:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90]
对元素中序遍历: 30 50 80 90
----------------------------------
found
删除测试
删除已满叶节点
删除已满叶节点元素1
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(80);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90]
对元素中序遍历: 30 50 80 90
----------------------------------
删除:80
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=90,value2=0]
对元素中序遍历: 30 50 90
----------------------------------
删除已满叶节点元素2
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(90);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90]
对元素中序遍历: 30 50 80 90
----------------------------------
删除:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0]
对元素中序遍历: 30 50 80
----------------------------------
删除未满叶节点且其父节点为2-节点
若兄弟节点已满,则重新分配
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(30);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90]
对元素中序遍历: 30 50 80 90
----------------------------------
删除:30
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=90,value2=0]
对元素中序遍历: 50 80 90
----------------------------------
若兄弟节点未满(完全二叉树),则循环合并兄弟和父节点
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
System.out.println(tree);
tree.del(40);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90
----------------------------------
删除:40
对节点层次遍历:[value1=50,value2=80] [value1=20,value2=30] [value1=70,value2=0] [value1=90,value2=0]
对元素中序遍历: 20 30 50 70 80 90
----------------------------------
若循环过程中遇到满兄弟节点,则重新分配
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(40);
System.out.println(tree);
----------------------------------
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:40
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=95,value2=0] [value1=20,value2=30] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 50 70 80 90 95 100
----------------------------------
删除未满叶节点且其父节点为3-节点
删除左未满叶节点
若中节点未满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(70);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:70
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=80,value2=90] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 80 90 95 100
----------------------------------
若中节点已满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(94);
System.out.println(tree);
tree.del(70);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:94
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=94] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 94 95 100
----------------------------------
删除:70
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=90,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=80,value2=0] [value1=94,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 80 90 94 95 100
----------------------------------
删除中未满叶节点
若左节点未满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(90);
System.out.println(tree);
----------------------------------
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=80] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 95 100
----------------------------------
若左节点已满,右节点未满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(65);
System.out.println(tree);
tree.del(90);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:65
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 65 70 80 90 95 100
----------------------------------
删除:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=95,value2=100]
对元素中序遍历: 20 30 40 50 65 70 80 95 100
----------------------------------
若左右节点已满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(65);
tree.add(105);
System.out.println(tree);
tree.del(90);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:65 插入:105
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=90,value2=0] [value1=100,value2=105]
对元素中序遍历: 20 30 40 50 65 70 80 90 95 100 105
----------------------------------
删除:90
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=100] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=95,value2=0] [value1=105,value2=0]
对元素中序遍历: 20 30 40 50 65 70 80 95 100 105
----------------------------------
删除右未满叶节点
若中节点未满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(100);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=95]
对元素中序遍历: 20 30 40 50 70 80 90 95
----------------------------------
若中节点已满
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(94);
System.out.println(tree);
tree.del(100);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:94
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=94] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 94 95 100
----------------------------------
删除:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=94] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=95,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 94 95
----------------------------------
删除非叶2-节点
转换为删除其右子树最小叶节点
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(30);
System.out.println(tree);
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:30
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=95,value2=0] [value1=20,value2=40] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 40 50 70 80 90 95 100
----------------------------------
删除非叶3-节点
删除元素1,转换为删除其中子树最小叶节点
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(80);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:80
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=90] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 90 95 100
----------------------------------
删除元素2,转换为删除其右子树最小叶节点
Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(95);
System.out.println(tree);
打印如下
插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0]
对元素中序遍历: 20 30 40 50 70 80 90 95 100
----------------------------------
删除:95
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=100]
对元素中序遍历: 20 30 40 50 70 80 90 100
----------------------------------