实现2-3树的插入有两种方式,一种就是在普通的2-3树上的操作,情况挺复杂的,尤其是在对父节点是3-节点的3-节点进行插入的时候,还要用到临时的4-节点,还有一种就是借用红黑树来实现。
与2-3树一一对应的红黑树
如果红黑树的右节点必须是黑色,而左节点不做要求的话:
1.左节点是黑色,那么此节点对应的就是一个2-节点
2.左节点为红色,那么它与此节点同层,对应3-节点,如图可以很清晰的看出节点之间的对应关系
这样就将一棵2-3树与红黑树相对应起来,就可以借用红黑树的操作了
正儿八经2-3树的插入过程
在进行插入操作时,要时时刻刻维持树的平衡,比如出现这种狗样子的树:
这三种情况是明显不符合2-3树的气质的,要进行相应的旋转变色操作变成下面这三种:
正儿八经2-3树的代码
两部分,这部分是2-3树的插入的实现,第二部分为相应的测试代码
package algorithm; /********************************************* * A4.java * Author: jkbao * Created on: 2018年4月18日 ********************************************/ import java.util.LinkedList; import java.util.Queue; public class A4 { private Node root; private static final boolean RED = true; private static final boolean BLACK = false; private class Node { Object val; Node left, right; // int N; boolean color; Node(Object val, boolean color) { this.val = val; // this.N = N; this.color = color; } } private boolean isRed(Node x) { if (x == null) return false; return x.color == RED; } private Node rotateLeft(Node h) { Node x = h.right; h.right = x.left; x.left = h; x.color = h.color; h.color = RED; return x; } private Node rotateRight(Node h) { Node x = h.left; h.left = x.right; x.right = h; x.color = h.color; h.color = RED; return x; } private void filpColors(Node h) { h.color = RED; h.left.color = BLACK; h.right.color = BLACK; } public void put(Object val) { root = put(root, val); root.color = BLACK; } private Node put(Node h, Object val) { if (h == null) return new Node(val, RED); if ((int) val < (int) h.val) { h.left = put(h.left, val); // System.out.println((int)h.val); // System.out.println((int)h.left.val); } else if ((int) val > (int) h.val) h.right = put(h.right, val); if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); if (isRed(h.left) && isRed(h.right)) filpColors(h); return h; }
第一部分到这里结束,关键的就是put方法的实现,其实在二叉平衡树里面这个方法的实现都差不多,不同的是在递归调用put方法之后的相应的旋转变色操作。
第二部分,以序列 { 'S', 'E', 'A', 'R', 'C', 'H', 'X', 'M' } 为例,一步步打印出二叉树节点的插入过程,打印的实现用了广度优先遍历算法,分层打印的实现也用到了一个小技巧。
正儿八经测试的代码
public static void main(String args[]) { char arr[] = { 'S', 'E', 'A', 'R', 'C', 'H', 'X', 'M' }; A4 a = new A4(); a.put((int) arr[0]); for (int i = 1; i < arr.length; i++) { //System.out.println(a.level(a.root)); a.bfs(a.root); System.out.println("****************"); a.put((int) arr[i]); } // System.out.println(a.level(a.root)); a.bfs(a.root); } public int level(Node no) { if (no == null) { return 0; } if (no.color == BLACK) return 1 + level(no.right); return level(no.right); } public void bfs(Node rootNode) { Queue<Node> queue = new LinkedList<>(); if (rootNode == null) return; if (root.left != null && root.left.color == RED) queue.offer(root.left); queue.offer(rootNode); //这个队列用于分层打印的实现 Queue<Node> tempQue = new LinkedList<>(); while (!queue.isEmpty()) { Node node = queue.poll(); System.out.print((char) (int) node.val); if (node.color == RED) System.out.print("/"); if (node.color == RED) { if (node.left != null && node.left.left != null && node.left.left.color == RED) tempQue.offer(node.left.left); if (node.left != null) tempQue.offer(node.left); if (node.right != null && node.right.left != null && node.right.left.color == RED) tempQue.offer(node.right.left); if (node.right != null) tempQue.offer(node.right); } else { if (node.left != null && node.left.color == BLACK) { if (node.left.left != null && node.left.left.color == RED) { tempQue.offer(node.left.left); } tempQue.offer(node.left); } if (node.right != null && node.right.left != null && node.right.left.color == RED) tempQue.offer(node.right.left); if (node.right != null) tempQue.offer(node.right); } //队列为空说明这一层打印完了 if (queue.isEmpty()) { System.out.println(); queue = tempQue; tempQue = new LinkedList<>(); } } } }
正儿八经的测试结果比对
插入过程图 vs 测试结果图(E/ 为红色(节点后面有个斜杠就是红色),S为黑色(没有斜杠))