2-3树的特点:
1.2-节点至少有一个数据项和两个节点,它小于它的右孩子数据大于左孩子数据
2.3-节点必定包含两个数据S,L,它的左孩子的数据小于S,中孩子的数据大于S小于L,而右孩子大的数据于L
3.叶子可以包含一个或者两个数据
2-节点
3-节点
在2-3树种每一节点必定有两个或3个子节点,不存在一个节点有一个子节点的情况,当然是尾节点除外,尾节点那就是没有节点
然后附上一个2-3树
写一个树最复杂的便是删除和插入。
2-3树的节点:
我们由定义可知,一个2-3树节点可能由一个数据也有可能有两个数据所以我们定义为两个数据,分别为左数据和右数据
当只有一个数据时那我们必定存在left中。子节点也有2或者3个所以我们定义为3个子节点和一个父节点
package Tree;
public class tree2_3Node<T> {
tree2_3Node<T> parent;//父节点
tree2_3Node<T> leftChild;//左孩子
tree2_3Node<T> rightChild;//中间孩子
tree2_3Node<T> midChild;//右孩子
T left;
T right;
}
判断某一个节点有几个子节点:
这个逻辑很简单,
如果它有中子节点说明是三个
如果它没有中子节点但是有左子节点,说明是两个
不符合上面中况说明没有子节点
代码:
private int numberOfChild(tree2_3Node<Integer> node) {
//有中节点
if (node.midChild != null) {
return 3;
}
//没有中节点但是有左节点
if (node.midChild == null && node.leftChild != null) {
return 2;
}
//都没有
return 0;
}
2-3树遍历:
遍历我只写按层遍历,其实就是和二叉树没有区别
先找两个队列去q1、q2,将将第一层存入q1中,输入且存入q2中,在将q2中的子孩子存入q1中循环即可
// 按层遍历和二叉树相同
public void ergodic() {
tree2_3Node<Integer> node = treeRoot;
// 两个队列
ArrayList<tree2_3Node<Integer>> queue1 = new ArrayList<>();
ArrayList<tree2_3Node<Integer>> queue2 = new ArrayList<>();
// 先把第一个队列加入q1中
queue1.add(node);
// 当q1中无数据说明遍历完成
while (!queue1.isEmpty()) {
// 输出q1中数据,并将节点加入q2中
while (!queue1.isEmpty()) {
// 判断该节点有几个数据并输出
if (queue1.get(0).right != null) {
System.out.print(queue1.get(0).left + "/" + (queue1.get(0).right + " "));
} else {
System.out.print(queue1.get(0).left + " ");
}
queue2.add(queue1.remove(0));
}
// 按层输出
System.out.println();
// 将q2的所有子节点加入q1中,并移除
while (!queue2.isEmpty()) {
if (numberOfChild(queue2.get(0)) == 3) {
queue1.add(queue2.get(0).leftChild);
queue1.add(queue2.get(0).midChild);
queue1.add(queue2.get(0).rightChild);
} else if (numberOfChild(queue2.get(0)) == 2) {
queue1.add(queue2.get(0).leftChild);
queue1.add(queue2.get(0).rightChild);
}
queue2.remove(0);
}
}
}
2-3树插入:
让我们看看2-3树是如何插入的。
二叉树插入是由顶部开始向下插入,而2-3树从底部开始插入
我们来看插入过程
a.首先只有一个数据100
b.然后我们插入第二个数据300,因为一个节点可以存两个数据,所以它们都存在一个节点中。
c.我们存入第三个数据,这是一个节点已经存不下了,那我们如何办呢?这时我们将三个数据中处于中间的数据向上移动变成它们的父节点。记住当在一个节点存第三个数据时,我们往上一层移动的必定是第三个数据。
改变前 改变后
2-3树插入的情况可以分为3中:
a.插入的节点只有一个数据
这时我们直接将数据插入到这个节点中,两个数据进行比较,较大的数存入left中小数存入right中
像这个树中插入650即之间插入即可也不用改变什么。这也是最想遇到的情况
b.插入节点有三个数据但是其父节点只有两个孩子
如这个时候我们把950插入这颗树中,但是这个节点已经有两个数据了,那我们应该如何呢?
这个时候我们就需要将900移到其父节点的位置,而800和950分别变成两个父节点的子节点
这便是插入后的情况
c当我们插入一个数据时,其节点为2个数据,其父节点也是两个数据
比如我们插入90个数据,就变成如图的样子
这个时候我们首先不用管其父节点,先把自己的事情解决了再说其父节点的事情,这个时候我们将它看成b中情况一样进行操作
然后变成如图的样子,这个时候我们发现父节点不满足要求了,所以我们将其父节点使用b操作进行一次操作
父节点看作新插入了一个数据
改变后
如果其父节点的父节点还是有2个数据那么继续向上移动知道完成
我们也可以将插入看出两中情况插入数据的节点为两个数据或者插入数据节点为一个数据
插入数据的代码:
// 插入
public void insertData(Integer data) {
// 第一次插入数据时,定义树根
if (treeRoot == null) {
treeRoot = new tree2_3Node<Integer>(data);
return;
}
// 找到应该插入的节点
tree2_3Node<Integer> insertNode = findTreeNode(data);
if (insertNode == null) {
System.err.println("树中已有该数值插入失败!");
return;
}
insertData(data, insertNode);
}
private void insertData(Integer data, tree2_3Node<Integer> insertNode) {
tree2_3Node<Integer> nodeFather = null;
tree2_3Node<Integer> newnode = insertNode;
// 插入节点之后该节点的父亲的左节点,中节点,和右节点的值
// 将插入的数据转化为int 插入节点的左节点也转化为int
int num = data.intValue();
int num2 = insertNode.left.intValue();
// 判断是不是叶子节点
if (numberOfChild(insertNode) == 0) {
// 插入是叶子节点
// 判断插入节点时,此节点有几个数据
if (insertNode.right == null) {
// 一个数据
// 插入数据
if (num < num2) {
insertNode.left = num;
insertNode.right = num2;
} else {
insertNode.right = num;
}
// treeRoot=insertNode;
// System.out.println(treeRoot.left+