tree
几种树:
(1)平衡树:
package com.jp.algorithm.tree; import java.util.Iterator; import java.util.NoSuchElementException; /** * 平衡二叉树 * * 定义:首先它是一种特殊的二叉排序树,其次它的左子树和右子树都是平衡二叉树, 且左子树和右子树的深度之差不超过1 * 平衡因子:可以定义为左子树的深度减去右子树的深度 * * 平衡二叉树是对二叉排序树的优化,防止二叉排序树在最坏情况下平均查找时间为n, 二叉排序树在此时形如一个单链表 * 平衡二叉树查找元素的次数不超过树的深度,时间复杂度为logN * * @author peng.jia * * @param <E> */ public class AVLTree<E> { /** * 根节点 */ private Entry<E> root = null; /** * 树中元素个数 */ private int size = 0; public AVLTree() { } public int size() { return size; } /** * @param p * 最小旋转子树的根节点 向左旋转之后p移到p的左子树处,p的右子树B变为此最小子树根节点, B的左子树变为p的右子树 比如: * A(-2) B(1) \ 左旋转 / \ B(0) ----> A(0) \ / \ \ \ BL(0) BR(0) * BL(0) BR(0) 旋转之后树的深度之差不超过1 */ private void rotateLeft(Entry<E> p) { System.out.println("绕" + p.element + "左旋"); if (p != null) { Entry<E> r = p.right; p.right = r.left; // 把p右子树的左节点嫁接到p的右节点,如上图,把BL作为A的右子节点 if (r.left != null) // 如果B的左节点BL不为空,把BL的父节点设为A r.left.parent = p; r.parent = p.parent; // A的父节点设为B的父节点 if (p.parent == null) // 如果p是根节点 root = r; // r变为父节点,即B为父节点 else if (p.parent.left == p) // 如果p是左子节点 p.parent.left = r; // p的父节点的左子树为r else // 如果p是右子节点 p.parent.right = r; // p的父节点的右子树为r r.left = p; // p变为r的左子树,即A为B的左子树 p.parent = r; // 同时更改p的父节点为r,即A的父节点为B } } /** * @param p * 最小旋转子树的根节点 向右旋转之后,p移到p的右子节点处,p的左子树B变为最小旋转子树的根节点 B的右子节点变为p的左节点、 * 例如: A(2) B(-1) / 右旋转 / \ B(0) ------> / A(0) / \ / / BL(0) * BR(0) BL(0) BR(0) */ private void rotateRight(Entry<E> p) { System.out.println("绕" + p.element + "右旋"); if (p != null) { Entry<E> l = p.left; p.left = l.right; // 把B的右节点BR作为A的左节点 if (l.right != null) // 如果BR不为null,设置BR的父节点为A l.right.parent = p; l.parent = p.parent; // A的父节点赋给B的父节点 if (p.parent == null) // 如果p是根节点 root = l; // B为根节点 else if (p.parent.right == p) // 如果A是其父节点的左子节点 p.parent.right = l; // B为A的父节点的左子树 else // 如果A是其父节点的右子节点 p.parent.left = l; // B为A的父节点的右子树 l.right = p; // A为B的右子树 p.parent = l; // 设置A的父节点为B } } /** * 平衡而二叉树的插入操作 基本原理为: 1.首先如同二叉排序树一般,找到其要插入的节点的位置,并把元素插入其中; * 2.自下向上进行回溯,回溯做两个事情: (1)其一是修改祖先节点的平衡因子,当插入 一个节点时只有根节点到插入节点 * 的路径中的节点的平衡因子会被改变,而且改变的原则是当插入节点在某节点(称为A) 的左子树 * 中时,A的平衡因子(称为BF)为BF+1,当插入节点在A的右子树中时A的BF-1, 而判断插入节点在左子树中还是右子树中只要简单的比较它与A的大小 * (2)在改变祖先节点的平衡因子的同时,找到最近一个平衡因子大于2或小于-2的节点, * 从这个节点开始调整最小不平衡树进行旋转调整,关于如何调整见下文。 * 由于调整后,最小不平衡子树的高度与插入节点前的高度相同,故不需继续要调整祖先节点。 * 这里还有一个特殊情况,如果调整BF时,发现某个节点的BF变为0了,则停止向上继续调整, * 因为这表明此节点中高度小的子树增加了新节点,高度不变,那么祖先节点的BF自然不变。 * * */ public boolean add(E element) { Entry<E> t = root; if (t == null) { root = new Entry<E>(element, null); size = 1; return true; } int cmp; Entry<E> parent; // 保存t的父节点 Comparable<? super E> e = (Comparable<? super E>) element; // 从根节点向下搜索,找到插入位置 do { parent = t; cmp = e.compareTo(t.element); if (cmp < 0) { t = t.left; } else if (cmp > 0) { t = t.right; } else { return false; } } while (t != null); Entry<E> child = new Entry(element, parent); if (cmp < 0) { parent.left = child; } else { parent.right = child; } // 自下向上回溯,查找最近不平衡节点 while (parent != null) { cmp = e.compareTo(parent.element); if (cmp < 0) { // 插入节点在parent的左子树中 parent.balance++; } else { // 插入节点在parent的右子树中 parent.balance--; } if (parent.balance == 0) { // 此节点的balance为0,不再向上调整BF值,且不需要旋转 break; } if (Math.abs(parent.balance) == 2) { // 找到最小不平衡子树根节点 fixAfterInsertion(parent); break; // 不用继续向上回溯 } parent = parent.parent; } size++; return true; } /** * 调整的方法: 1.当最小不平衡子树的根(以下简称R)为2时,即左子树高于右子树: 如果R的左子树的根节点的BF为1时,做右旋; * 如果R的左子树的根节点的BF为-1时,先左旋然后再右旋 * * 2.R为-2时,即右子树高于左子树: 如果R的右子树的根节点的BF为1时,先右旋后左旋 如果R的右子树的根节点的BF为-1时,做左旋 * * 至于调整之后,各节点的BF变化见代码 */ private void fixAfterInsertion(Entry<E> p) { if (p.balance == 2) { leftBalance(p); } if (p.balance == -2) { rightBalance(p); } } /** * 做左平衡处理 平衡因子的调整如图: t rd / \ / \ l tr 左旋后右旋 l t / \ -------> / \ / \ ll rd * ll rdl rdr tr / \ rdl rdr * * 情况2(rd的BF为0) * * * t rd / \ / \ l tr 左旋后右旋 l t / \ -------> / \ \ ll rd ll rdl tr / rdl * * 情况1(rd的BF为1) * * * t rd / \ / \ l tr 左旋后右旋 l t / \ -------> / / \ ll rd ll rdr tr \ rdr * * 情况3(rd的BF为-1) * * * t l / 右旋处理 / \ l ------> ll t / \ / ll rd rd 情况4(L等高) */ private boolean leftBalance(Entry<E> t) { boolean heightLower = true; Entry<E> l = t.left; switch (l.balance) { case LH: // 左高,右旋调整,旋转后树的高度减小 t.balance = l.balance = EH; rotateRight(t); break; case RH: // 右高,分情况调整 Entry<E> rd = l.right; switch (rd.balance) { // 调整各个节点的BF case LH: // 情况1 t.balance = RH; l.balance = EH; break; case EH: // 情况2 t.balance = l.balance = EH; break; case RH: // 情况3 t.balance = EH; l.balance = LH; break; } rd.balance = EH; rotateLeft(t.left); rotateRight(t); break; case EH: // 特殊情况4,这种情况在添加时不可能出现,只在移除时可能出现,旋转之后整体树高不变 l.balance = RH; t.balance = LH; rotateRight(t); heightLower = false; break; } return heightLower; } /** * 做右平衡处理 平衡因子的调整如图: t ld / \ / \ tl r 先右旋再左旋 t r / \ --------> / \ / \ ld * rr tl ldl ldr rr / \ ldl ldr 情况2(ld的BF为0) * * t ld / \ / \ tl r 先右旋再左旋 t r / \ --------> / \ \ ld rr tl ldl rr / ldl * 情况1(ld的BF为1) * * t ld / \ / \ tl r 先右旋再左旋 t r / \ --------> / / \ ld rr tl ldr rr \ ldr * 情况3(ld的BF为-1) * * t r \ 左旋 / \ r -------> t rr / \ \ ld rr ld 情况4(r的BF为0) */ private boolean rightBalance(Entry<E> t) { boolean heightLower = true; Entry<E> r = t.right; switch (r.balance) { case LH: // 左高,分情况调整 Entry<E> ld = r.left; switch (ld.balance) { // 调整各个节点的BF case LH: // 情况1 t.balance = EH; r.balance = RH; break; case EH: // 情况2 t.balance = r.balance = EH; break; case RH: // 情况3 t.balance = LH; r.balance = EH; break; } ld.balance = EH; rotateRight(t.right); rotateLeft(t); break; case RH: // 右高,左旋调整 t.balance = r.balance = EH; rotateLeft(t); break; case EH: // 特殊情况4 r.balance = LH; t.balance = RH; rotateLeft(t); heightLower = false; break; } return heightLower; } /** * 查找指定元素,如果找到返回其Entry对象,否则返回null */ private Entry<E> getEntry(Object element) { Entry<E> tmp = root; Comparable<? super E> e = (Comparable<? super E>) element; int c; while (tmp != null) { c = e.compareTo(tmp.element); if (c == 0) { return tmp; } else if (c < 0) { tmp = tmp.left; } else { tmp = tmp.right; } } return null; } /** * 平衡二叉树的移除元素操作 * */ public boolean remove(Object o) { Entry<E> e = getEntry(o); if (e != null) { deleteEntry(e); return true; } return false; } private void deleteEntry(Entry<E> p) { size--; // 如果p左右子树都不为空,找到其直接后继,替换p,之后p指向s,删除p其实是删除s // 所有的删除左右子树不为空的节点都可以调整为删除左右子树有其一不为空,或都为空的情况。 if (p.left != null && p.right != null) { Entry<E> s = successor(p); p.element = s.element; p = s; } Entry<E> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { // 如果其左右子树有其一不为空 replacement.parent = p.parent; if (p.parent == null) // 如果p为root节点 root = replacement; else if (p == p.parent.left) // 如果p是左孩子 p.parent.left = replacement; else // 如果p是右孩子 p.parent.right = replacement; p.left = p.right = p.parent = null; // p的指针清空 // 这里更改了replacement的父节点,所以可以直接从它开始向上回溯 fixAfterDeletion(replacement); } else if (p.parent == null) { // 如果全树只有一个节点 root = null; } else { // 左右子树都为空 fixAfterDeletion(p); // 这里从p开始回溯 if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } } /** * 返回以中序遍历方式遍历树时,t的直接后继 */ static <E> Entry<E> successor(Entry<E> t) { if (t == null) return null; else if (t.right != null) { // 往右,然后向左直到尽头 Entry<E> p = t.right; while (p.left != null) p = p.left; return p; } else { // right为空,如果t是p的左子树,则p为t的直接后继 Entry<E> p = t.parent; Entry<E> ch = t; while (p != null && ch == p.right) { // 如果t是p的右子树,则继续向上搜索其直接后继 ch = p; p = p.parent; } return p; } } /** * 删除某节点p后的调整方法: 1.从p开始向上回溯,修改祖先的BF值,这里只要调整从p的父节点到根节点的BF值, * 调整原则为,当p位于某祖先节点(简称A)的左子树中时,A的BF减1,当p位于A的 * 右子树中时A的BF加1。当某个祖先节点BF变为1或-1时停止回溯,这里与插入是相反的, * 因为原本这个节点是平衡的,删除它的子树的某个节点并不会改变它的高度 * * 2.检查每个节点的BF值,如果为2或-2需要进行旋转调整,调整方法如下文, * 如果调整之后这个最小子树的高度降低了,那么必须继续从这个最小子树的根节点(假设为B)继续 * 向上回溯,这里和插入不一样,因为B的父节点的平衡性因为其子树B的高度的改变而发生了改变, 那么就可能需要调整,所以删除可能进行多次的调整。 * */ private void fixAfterDeletion(Entry<E> p) { boolean heightLower = true; // 看最小子树调整后,它的高度是否发生变化,如果减小,继续回溯 Entry<E> t = p.parent; Comparable<? super E> e = (Comparable<? super E>) p.element; int cmp; // 自下向上回溯,查找不平衡的节点进行调整 while (t != null && heightLower) { cmp = e.compareTo(t.element); /** * 删除的节点是右子树,等于的话,必然是删除的某个节点的左右子树不为空的情况 例如: 10 / \ 5 15 / \ 3 6 * 这里删除5,是把6的值赋给5,然后删除6,这里6是p,p的父节点的值也是6。 而这也是右子树的一种 */ if (cmp >= 0) { t.balance++; } else { t.balance--; } if (Math.abs(t.balance) == 1) { // 父节点经过调整平衡因子后,如果为1或-1,说明调整之前是0,停止回溯。 break; } Entry<E> r = t; // 这里的调整跟插入一样 if (t.balance == 2) { heightLower = leftBalance(r); } else if (t.balance == -2) { heightLower = rightBalance(r); } t = t.parent; } } private static final int LH = 1; // 左高 private static final int EH = 0; // 等高 private static final int RH = -1; // 右高 /** * 树节点,为方便起见不写get,Set方法 */ static class Entry<E> { E element; Entry<E> parent; Entry<E> left; Entry<E> right; int balance = EH; // 平衡因子,只能为1或0或-1 public Entry(E element, Entry<E> parent) { this.element = element; this.parent = parent; } public Entry() { } @Override public String toString() { return element + " BF=" + balance; } } // 返回中序遍历此树的迭代器,返回的是一个有序列表 private class BinarySortIterator implements Iterator<E> { Entry<E> next; Entry<E> lastReturned; public BinarySortIterator() { Entry<E> s = root; if (s != null) { while (s.left != null) { // 找到中序遍历的第一个元素 s = s.left; } } next = s; // 赋给next } @Override public boolean hasNext() { return next != null; } @Override public E next() { Entry<E> e = next; if (e == null) throw new NoSuchElementException(); next = successor(e); lastReturned = e; return e.element; } @Override public void remove() { if (lastReturned == null) throw new IllegalStateException(); // deleted entries are replaced by their successors if (lastReturned.left != null && lastReturned.right != null) next = lastReturned; deleteEntry(lastReturned); lastReturned = null; } } public Iterator<E> itrator() { return new BinarySortIterator(); } private int treeHeight(Entry<E> p) { if (p == null) { return -1; } return 1 + Math.max(treeHeight(p.left), treeHeight(p.right)); } // 测试,你也可以任意测试 public static void main(String[] args) { AVLTree<Integer> tree = new AVLTree<Integer>(); System.out.println("------添加------"); tree.add(50); System.out.print(50 + " "); tree.add(66); System.out.print(66 + " "); for (int i = 0; i < 10; i++) { int ran = (int) (Math.random() * 100); System.out.print(ran + " "); tree.add(ran); } System.out.println("\n------删除------"); tree.remove(50); tree.remove(66); System.out.println(); Iterator<Integer> it = tree.itrator(); while (it.hasNext()) { System.out.print(it.next() + " "); } } }
(2)B树:
package com.jp.algorithm.tree; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Random; /** * B树 * * @author peng.jia * * @param <K> * - 键类型 * @param <V> * - 值类型 */ public class BTree<K, V> { /** * 在B树节点中搜索给定键值的返回结果。 * <p/> * 该结果有两部分组成。第一部分表示此次查找是否成功, 如果查找成功,第二部分表示给定键值在B树节点中的位置, * 如果查找失败,第二部分表示给定键值应该插入的位置。 */ private static class SearchResult { private boolean result; private int index; public SearchResult(boolean result, int index) { this.result = result; this.index = index; } public boolean getResult() { return result; } public int getIndex() { return index; } } /** * 为了简单起见,暂时只支持整型的key, 等到工作完成后,支持泛型。 * <p/> * * TODO 需要考虑并发情况下的存取。 */ private static class BTreeNode { /** 节点的关键字,以非降序存放 */ private List<Integer> keys; /** 内节点的子节点 */ private List<BTreeNode> children; /** 是否为叶子节点 */ private boolean leaf; public BTreeNode() { keys = new ArrayList<Integer>(); children = new ArrayList<BTreeNode>(); leaf = false; } public boolean isLeaf() { return leaf; } public void setLeaf(boolean leaf) { this.leaf = leaf; } /** * 返回关键字的个数。如果是非叶子节点,该节点的 子节点个数为({@link #size()} + 1)。 * * @return 关键字的个数 */ public int size() { return keys.size(); } /** * 在节点中查找给定的<code>key</code>,如果节点中存在给定的 <code>key</code>,则返回一个 * <code>SearchResult</code>, 标识此次查找成功,给定<code>key</code>在节点中的索引和给定 * <code>key</code>对应的值。如果不存在,则返回<code>SearchResult</code> 标识此次查找失败,给定 * <code>key</code>应该插入的位置,该<code>key</code> 对应的值为null。 * <p/> * 如果查找失败,返回结果中的索引域为[0, {@link #size()}]; 如果查找成功,返回结果中的索引域为[0, * {@link #size()} - 1] * <p/> * 这是一个二分查找算法,可以保证时间复杂度为O(log(t))。 * * @param key * - 给定的键值 * @return - 查找结果 */ public SearchResult searchKey(Integer key) { int l = 0; int h = keys.size() - 1; int mid = 0; while (l <= h) { mid = (l + h) / 2; // 先这么写吧,BTree实现中,l+h不可能溢出 if (keys.get(mid) == key) break; else if (keys.get(mid) > key) h = mid - 1; else // if(keys.get(mid) < key) l = mid + 1; } boolean result = false; int index = 0; if (l <= h) // 说明查找成功 { result = true; index = mid; // index表示元素所在的位置 } else { result = false; index = l; // index表示元素应该插入的位置 } return new SearchResult(result, index); } /** * 将给定的<code>key</code>追加到节点的末尾, 一定要确保调用该方法之后,节点中的关键字还是 以非降序存放。 * * @param key * - 给定的键值 */ public void addKey(Integer key) { keys.add(key); } /** * 删除给定索引的键值。 * <p/> * 你需要自己保证给定的索引是合法的。 * * @param index * - 给定的索引 */ public void removeKey(int index) { keys.remove(index); } /** * 得到节点中给定索引的键值。 * <p/> * 你需要自己保证给定的索引是合法的。 * * @param index * - 给定的索引 * @return 节点中给定索引的键值 */ public Integer keyAt(int index) { return keys.get(index); } /** * 在该节点中插入给定的<code>key</code>, 该方法保证插入之后,其键值还是以非降序存放。 * <p/> * 不过该方法的时间复杂度为O(t)。 * <p/> * TODO 需要考虑键值是否可以重复。 * * @param key * - 给定的键值 */ public void insertKey(Integer key) { SearchResult result = searchKey(key); insertKey(key, result.getIndex()); } /** * 在该节点中给定索引的位置插入给定的<code>key</code>, 你需要自己保证<code>key</code>插入了正确的位置。 * * @param key * - 给定的键值 * @param index * - 给定的索引 */ public void insertKey(Integer key, int index) { /* * TODO 通过新建一个ArrayList来实现插入真的很恶心,先这样吧 要是有类似C中的reallocate就好了。 */ List<Integer> newKeys = new ArrayList<Integer>(); int i = 0; // index = 0或者index = keys.size()都没有问题 for (; i < index; ++i) newKeys.add(keys.get(i)); newKeys.add(key); for (; i < keys.size(); ++i) newKeys.add(keys.get(i)); keys = newKeys; } /** * 返回节点中给定索引的子节点。 * <p/> * 你需要自己保证给定的索引是合法的。 * * @param index * - 给定的索引 * @return 给定索引对应的子节点 */ public BTreeNode childAt(int index) { if (isLeaf()) throw new UnsupportedOperationException( "Leaf node doesn't have children."); return children.get(index); } /** * 将给定的子节点追加到该节点的末尾。 * * @param child * - 给定的子节点 */ public void addChild(BTreeNode child) { children.add(child); } /** * 删除该节点中给定索引位置的子节点。 </p> 你需要自己保证给定的索引是合法的。 * * @param index * - 给定的索引 */ public void removeChild(int index) { children.remove(index); } /** * 将给定的子节点插入到该节点中给定索引 的位置。 * * @param child * - 给定的子节点 * @param index * - 子节点带插入的位置 */ public void insertChild(BTreeNode child, int index) { List<BTreeNode> newChildren = new ArrayList<BTreeNode>(); int i = 0; for (; i < index; ++i) newChildren.add(children.get(i)); newChildren.add(child); for (; i < children.size(); ++i) newChildren.add(children.get(i)); children = newChildren; } } private static final int DEFAULT_T = 2; /** B树的根节点 */ private BTreeNode root; /** 根据B树的定义,B树的每个非根节点的关键字数n满足(t - 1) <= n <= (2t - 1) */ private int t = DEFAULT_T; /** 非根节点中最小的键值数 */ private int minKeySize = t - 1; /** 非根节点中最大的键值数 */ private int maxKeySize = 2 * t - 1; public BTree() { root = new BTreeNode(); root.setLeaf(true); } public BTree(int t) { this(); this.t = t; minKeySize = t - 1; maxKeySize = 2 * t - 1; } /** * 搜索给定的<code>key</code>。 * <p/> * TODO 需要重新定义返回结果,应该返回 <code>key</code>对应的值。 * * @param key * - 给定的键值 * @return TODO 得返回值类型 */ public int search(Integer key) { return search(root, key); } /** * 在以给定节点为根的子树中,递归搜索 给定的<code>key</code> * * @param node * - 子树的根节点 * @param key * - 给定的键值 * @return TODO */ private static int search(BTreeNode node, Integer key) { SearchResult result = node.searchKey(key); if (result.getResult()) return result.getIndex(); else { if (node.isLeaf()) return -1; else search(node.childAt(result.getIndex()), key); } return -1; } /** * 分裂一个满子节点<code>childNode</code>。 * <p/> * 你需要自己保证给定的子节点是满节点。 * * @param parentNode * - 父节点 * @param childNode * - 满子节点 * @param index * - 满子节点在父节点中的索引 */ private void splitNode(BTreeNode parentNode, BTreeNode childNode, int index) { assert childNode.size() == maxKeySize; BTreeNode siblingNode = new BTreeNode(); siblingNode.setLeaf(childNode.isLeaf()); // 将满子节点中索引为[t, 2t - 2]的(t - 1)个关键字插入新的节点中 for (int i = 0; i < minKeySize; ++i) siblingNode.addKey(childNode.keyAt(t + i)); // 提取满子节点中的中间关键字,其索引为(t - 1) Integer key = childNode.keyAt(t - 1); // 删除满子节点中索引为[t - 1, 2t - 2]的t个关键字 for (int i = maxKeySize - 1; i >= t - 1; --i) childNode.removeKey(i); if (!childNode.isLeaf()) // 如果满子节点不是叶节点,则还需要处理其子节点 { // 将满子节点中索引为[t, 2t - 1]的t个子节点插入新的节点中 for (int i = 0; i < minKeySize + 1; ++i) siblingNode.addChild(childNode.childAt(t + i)); // 删除满子节点中索引为[t, 2t - 1]的t个子节点 for (int i = maxKeySize; i >= t; --i) childNode.removeChild(i); } // 将key插入父节点 parentNode.insertKey(key, index); // 将新节点插入父节点 parentNode.insertChild(siblingNode, index + 1); } /** * 在一个非满节点中插入给定的<code>key</code>。 * * @param node * - 非满节点 * @param key * - 给定的键值 */ private void insertNotFull(BTreeNode node, Integer key) { assert node.size() < maxKeySize; if (node.isLeaf()) // 如果是叶子节点,直接插入 node.insertKey(key); else { /* * 找到key在给定节点应该插入的位置,那么key应该插入 该位置对应的子树中 */ SearchResult result = node.searchKey(key); BTreeNode childNode = node.childAt(result.getIndex()); if (childNode.size() == 2 * t - 1) // 如果子节点是满节点 { // 则先分裂 splitNode(node, childNode, result.getIndex()); /* * 如果给定的key大于分裂之后新生成的键值,则需要插入该新键值的右边, 否则左边。 */ if (key > node.keyAt(result.getIndex())) childNode = node.childAt(result.getIndex() + 1); } insertNotFull(childNode, key); } } /** * 在B树中插入给定的<code>key</code>。 * * @param key * - 给定的键值 */ public void insert(Integer key) { if (root.size() == maxKeySize) // 如果根节点满了,则B树长高 { BTreeNode newRoot = new BTreeNode(); newRoot.setLeaf(false); newRoot.addChild(root); splitNode(newRoot, root, 0); root = newRoot; } insertNotFull(root, key); } /** * 从B树中删除一个给定的<code>key</code>。 * * @param key * - 给定的键值 */ public void delete(Integer key) { // root的情况还需要做一些特殊处理 delete(root, key); } /** * 从以给定<code>node</code>为根的子树中删除指定的<code>key</code>。 * <p/> * 删除的实现思想请参考《算法导论》第二版的第18章。 * <p/> * TODO 需要重构,代码太长了 * * @param node * - 给定的节点 * @param key * - 给定的键值 */ public void delete(BTreeNode node, Integer key) { // 该过程需要保证,对非根节点执行删除操作时,其关键字个数至少为t。 assert node.size() >= t || node == root; SearchResult result = node.searchKey(key); /* * 因为这是查找成功的情况,0 <= result.getIndex() <= (node.size() - 1), * 因此(result.getIndex() + 1)不会溢出。 */ if (result.getResult()) { // 1.如果关键字在节点node中,并且是叶节点,则直接删除。 if (node.isLeaf()) node.removeKey(result.getIndex()); else { // 2.a 如果节点node中前于key的子节点包含至少t个关键字 BTreeNode leftChildNode = node.childAt(result.getIndex()); if (leftChildNode.size() >= t) { // 使用leftChildNode中的最后一个键值代替node中的key node.removeKey(result.getIndex()); node.insertKey( leftChildNode.keyAt(leftChildNode.size() - 1), result.getIndex()); delete(leftChildNode, leftChildNode.keyAt(leftChildNode.size() - 1)); // node. } else { // 2.b 如果节点node中后于key的子节点包含至少t个关键字 BTreeNode rightChildNode = node .childAt(result.getIndex() + 1); if (rightChildNode.size() >= t) { // 使用rightChildNode中的第一个键值代替node中的key node.removeKey(result.getIndex()); node.insertKey(rightChildNode.keyAt(0), result.getIndex()); delete(rightChildNode, rightChildNode.keyAt(0)); } else // 2.c 前于key和后于key的子节点都只包含t-1个关键字 { node.removeKey(result.getIndex()); node.removeChild(result.getIndex() + 1); // 将key和rightChildNode中的键值合并进leftChildNode leftChildNode.addKey(key); for (int i = 0; i < rightChildNode.size(); ++i) leftChildNode.addKey(rightChildNode.keyAt(i)); // 将rightChildNode中的子节点合并进leftChildNode,如果有的话 if (!rightChildNode.isLeaf()) { for (int i = 0; i <= rightChildNode.size(); ++i) leftChildNode.addChild(rightChildNode .childAt(i)); } delete(leftChildNode, key); } } } } else { /* * 因为这是查找失败的情况,0 <= result.getIndex() <= node.size(), * 因此(result.getIndex() + 1)会溢出。 */ if (node.isLeaf()) // 如果关键字不在节点node中,并且是叶节点,则什么都不做,因为该关键字不在该B树中 { System.out.println("The key: " + key + " isn't in this BTree."); return; } BTreeNode childNode = node.childAt(result.getIndex()); if (childNode.size() >= t) delete(childNode, key); // 递归删除 else // 3 { // 先查找右边的兄弟节点 BTreeNode siblingNode = null; int siblingIndex = -1; if (result.getIndex() < node.size()) // 存在右兄弟节点 { if (node.childAt(result.getIndex() + 1).size() >= t) { siblingNode = node.childAt(result.getIndex() + 1); siblingIndex = result.getIndex() + 1; } } // 如果右边的兄弟节点不符合条件,则试试左边的兄弟节点 if (siblingNode == null) { if (result.getIndex() > 0) // 存在左兄弟节点 { if (node.childAt(result.getIndex() - 1).size() >= t) { siblingNode = node.childAt(result.getIndex() - 1); siblingIndex = result.getIndex() - 1; } } } // 3.a 有一个相邻兄弟节点至少包含t个关键字 if (siblingNode != null) { if (siblingIndex < result.getIndex()) // 左兄弟节点满足条件 { childNode.insertKey(node.keyAt(siblingIndex), 0); node.removeKey(siblingIndex); node.insertKey( siblingNode.keyAt(siblingNode.size() - 1), siblingIndex); siblingNode.removeKey(siblingNode.size() - 1); // 将左兄弟节点的最后一个孩子移到childNode if (!siblingNode.isLeaf()) { childNode.insertChild( siblingNode.childAt(siblingNode.size()), 0); siblingNode.removeChild(siblingNode.size()); } } else // 右兄弟节点满足条件 { childNode.insertKey(node.keyAt(result.getIndex()), childNode.size() - 1); node.removeKey(result.getIndex()); node.insertKey(siblingNode.keyAt(0), result.getIndex()); siblingNode.removeKey(0); // 将右兄弟节点的第一个孩子移到childNode // childNode.insertChild(siblingNode.childAt(0), // childNode.size() + 1); if (!siblingNode.isLeaf()) { childNode.addChild(siblingNode.childAt(0)); siblingNode.removeChild(0); } } delete(childNode, key); } else // 3.b 如果其相邻左右节点都包含t-1个关键字 { if (result.getIndex() < node.size()) // 存在右兄弟 { BTreeNode rightSiblingNode = node.childAt(result .getIndex() + 1); childNode.addKey(node.keyAt(result.getIndex())); node.removeKey(result.getIndex()); node.removeChild(result.getIndex() + 1); for (int i = 0; i < rightSiblingNode.size(); ++i) childNode.addKey(rightSiblingNode.keyAt(i)); if (!rightSiblingNode.isLeaf()) { for (int i = 0; i <= rightSiblingNode.size(); ++i) childNode.addChild(rightSiblingNode.childAt(i)); } } else // 存在左节点 { BTreeNode leftSiblingNode = node.childAt(result .getIndex() - 1); childNode.addKey(node.keyAt(result.getIndex() - 1)); node.removeKey(result.getIndex() - 1); node.removeChild(result.getIndex() - 1); for (int i = leftSiblingNode.size() - 1; i >= 0; --i) childNode.insertKey(leftSiblingNode.keyAt(i), 0); if (!leftSiblingNode.isLeaf()) { for (int i = leftSiblingNode.size(); i >= 0; --i) childNode.insertChild( leftSiblingNode.childAt(i), 0); } } // 如果node是root并且node不包含任何关键字了 if (node == root && node.size() == 0) root = childNode; delete(childNode, key); } } } } /** * 一个简单的层次遍历B树实现,用于输出B树。 * <p/> * TODO 待改进,使显示更加形象化。 */ public void output() { Queue<BTreeNode> queue = new LinkedList<BTreeNode>(); queue.offer(root); while (!queue.isEmpty()) { BTreeNode node = queue.poll(); for (int i = 0; i < node.size(); ++i) System.out.print(node.keyAt(i) + " "); System.out.println(); if (!node.isLeaf()) { for (int i = 0; i <= node.size(); ++i) queue.offer(node.childAt(i)); } } } public static void main(String[] args) { Random random = new Random(); BTree<Integer, Byte[]> btree = new BTree<Integer, Byte[]>(); for (int i = 0; i < 10; ++i) { int r = random.nextInt(100); System.out.println(r); btree.insert(r); } System.out.println("----------------------"); btree.output(); } }
(3)B+树:
package com.jp.algorithm.tree; import java.util.HashMap; import java.util.Map; /** * B+树 * * @author peng.jia * * @param <T> * @param <V> */ public class BPlusTree<T extends Comparable<T>, V> { /** * the branching factor of the tree, measuring the capacity of nodes. * * a) the number of children for internal node must be in scope [ * Math.ceil(factor/2), factor ]. b) the number of children for leaf node * must be in scope [ Math.floor(factor/2), factor-1 ] */ private int factor; private static final int DEFAULT_FACTOR = 5; private int MIN_CHILDREN_FOR_INTERNAL; private int MAX_CHILDREN_FOR_INTERNAL; private int MIN_FOR_LEAF; private int MAX_FOR_LEAF; private Node<T, V> root = null; public BPlusTree() { this(DEFAULT_FACTOR); } public BPlusTree(int factor) { this.factor = factor; this.MIN_CHILDREN_FOR_INTERNAL = Double.valueOf( Math.ceil(1.0 * this.factor / 2)).intValue(); this.MAX_CHILDREN_FOR_INTERNAL = this.factor; this.MIN_FOR_LEAF = Double.valueOf(Math.floor(1.0 * this.factor / 2)) .intValue(); this.MAX_FOR_LEAF = this.factor - 1; this.root = new LeafNode<T, V>(); } public void set(T key, V value) { if (key == null) throw new NullPointerException("must not be null for key."); Node node = this.root.insert(key, value); if (node != null) this.root = node; } public V get(T key) { return this.root.get(key); } public int height() { int height = 1; Node node = this.root; while (!(node instanceof LeafNode)) { height++; node = ((InternalNode) node).pointers[0]; } return height; } /** * the abstract node definition, define the operation of leaf node and * internal node. * * @author bo.fangbo * * @param <T> * @param <V> */ abstract class Node<T extends Comparable<T>, V> { protected Node<T, V> parent; protected Object[] keys; protected int size; /** * if new parent node is created when insert the key-value, the created * parent node is returned, in other case, this method return null. * * @param key * @param value * @return */ abstract Node<T, V> insert(T key, V value); abstract V get(T key); } /** * the internal node which manages the pointers. * * @author bo.fangbo * * @param <T> * @param <V> */ class InternalNode<T extends Comparable<T>, V> extends Node<T, V> { private Node<T, V>[] pointers; public InternalNode() { this.size = 0; this.pointers = new Node[MAX_CHILDREN_FOR_INTERNAL + 1]; this.keys = new Object[MAX_CHILDREN_FOR_INTERNAL]; } public Node<T, V> insert(T key, V value) { int i = 0; for (; i < this.size; i++) { if (key.compareTo((T) this.keys[i]) < 0) break; } return this.pointers[i].insert(key, value); } public V get(T key) { int i = 0; for (; i < this.size; i++) { if (key.compareTo((T) this.keys[i]) < 0) break; } return this.pointers[i].get(key); } /** * * @param key * @param leftChild * @param rightChild * @return */ private Node<T, V> insert(T key, Node<T, V> leftChild, Node<T, V> rightChild) { if (this.size == 0) { this.size++; this.pointers[0] = leftChild; this.pointers[1] = rightChild; this.keys[0] = key; leftChild.parent = this; rightChild.parent = this; return this; } Object[] newKeys = new Object[MAX_CHILDREN_FOR_INTERNAL + 1]; Node[] newPointers = new Node[MAX_CHILDREN_FOR_INTERNAL + 2]; int i = 0; for (; i < this.size; i++) { T curKey = (T) this.keys[i]; if (curKey.compareTo(key) > 0) break; } System.arraycopy(this.keys, 0, newKeys, 0, i); newKeys[i] = key; System.arraycopy(this.keys, i, newKeys, i + 1, this.size - i); System.arraycopy(this.pointers, 0, newPointers, 0, i + 1); newPointers[i + 1] = rightChild; System.arraycopy(this.pointers, i + 1, newPointers, i + 2, this.size - i); this.size++; if (this.size <= MAX_CHILDREN_FOR_INTERNAL) { System.arraycopy(newKeys, 0, this.keys, 0, this.size); System.arraycopy(newPointers, 0, this.pointers, 0, this.size + 1); return null; } int m = (this.size / 2); // split the internal node InternalNode<T, V> newNode = new InternalNode<T, V>(); newNode.size = this.size - m - 1; System.arraycopy(newKeys, m + 1, newNode.keys, 0, this.size - m - 1); System.arraycopy(newPointers, m + 1, newNode.pointers, 0, this.size - m); // reset the children's parent to the new node. for (int j = 0; j <= newNode.size; j++) { newNode.pointers[j].parent = newNode; } this.size = m; this.keys = new Object[MAX_CHILDREN_FOR_INTERNAL]; this.pointers = new Node[MAX_CHILDREN_FOR_INTERNAL]; System.arraycopy(newKeys, 0, this.keys, 0, m); System.arraycopy(newPointers, 0, this.pointers, 0, m + 1); if (this.parent == null) { this.parent = new InternalNode<T, V>(); } newNode.parent = this.parent; return ((InternalNode<T, V>) this.parent).insert((T) newKeys[m], this, newNode); } } /** * leaf node, store the keys and actual values. * * @author bo.fangbo * * @param <T> * @param <V> */ class LeafNode<T extends Comparable<T>, V> extends Node<T, V> { private Object[] values; public LeafNode() { this.size = 0; this.keys = new Object[MAX_FOR_LEAF]; this.values = new Object[MAX_FOR_LEAF]; this.parent = null; } public Node<T, V> insert(T key, V value) { Object[] newKeys = new Object[MAX_FOR_LEAF + 1]; Object[] newValues = new Object[MAX_FOR_LEAF + 1]; int i = 0; for (; i < this.size; i++) { T curKey = (T) this.keys[i]; if (curKey.compareTo(key) == 0) { this.values[i] = value; return null; } if (curKey.compareTo(key) > 0) break; } System.arraycopy(this.keys, 0, newKeys, 0, i); newKeys[i] = key; System.arraycopy(this.keys, i, newKeys, i + 1, this.size - i); System.arraycopy(this.values, 0, newValues, 0, i); newValues[i] = value; System.arraycopy(this.values, i, newValues, i + 1, this.size - i); this.size++; if (this.size <= MAX_FOR_LEAF) { System.arraycopy(newKeys, 0, this.keys, 0, this.size); System.arraycopy(newValues, 0, this.values, 0, this.size); return null; } // need split this node int m = this.size / 2; this.keys = new Object[MAX_FOR_LEAF]; this.values = new Object[MAX_FOR_LEAF]; System.arraycopy(newKeys, 0, this.keys, 0, m); System.arraycopy(newValues, 0, this.values, 0, m); LeafNode<T, V> newNode = new LeafNode<T, V>(); newNode.size = this.size - m; System.arraycopy(newKeys, m, newNode.keys, 0, newNode.size); System.arraycopy(newValues, m, newNode.values, 0, newNode.size); this.size = m; if (this.parent == null) { this.parent = new InternalNode<T, V>(); } newNode.parent = this.parent; return ((InternalNode<T, V>) this.parent).insert( (T) newNode.keys[0], this, newNode); } public V get(T key) { // two branch search if (this.size == 0) return null; int start = 0; int end = this.size; int middle = (start + end) / 2; while (start < end) { T middleKey = (T) this.keys[middle]; if (key.compareTo(middleKey) == 0) { break; } if (key.compareTo(middleKey) < 0) { end = middle; } else { start = middle; } middle = (start + end) / 2; } T middleKey = (T) this.keys[middle]; return middleKey.compareTo(key) == 0 ? (V) this.values[middle] : null; } } public static void main(String[] args) { BPlusTree<Integer, String> myTree = new BPlusTree<Integer, String>(8); int max = 1000000; long start = System.currentTimeMillis(); for (int i = 0; i < max; i++) { myTree.set(i, String.valueOf(i)); } System.out.println("time cost with BPlusTree: " + (System.currentTimeMillis() - start)); System.out.println("Data has been inserted into tree"); System.out.println("height: " + myTree.height()); start = System.currentTimeMillis(); Map<Integer, String> hashMap = new HashMap<Integer, String>(); for (int i = 0; i < max; i++) { hashMap.put(i, String.valueOf(i)); } System.out.println("time cost with HashMap: " + (System.currentTimeMillis() - start)); for (int i = 0; i < max; i++) { if (!String.valueOf(i).equals(myTree.get(i))) { System.err.println("error for: " + i); } } System.out.println("Success"); } }
(4)红黑树:
package com.jp.algorithm.tree; /** * 红黑树 * * @author peng.jia * */ public class RBTree { public static void main(String[] args) { RBTree rbTree = new RBTree(); rbTree.rbInsert(new Node(41)); rbTree.rbInsert(new Node(38)); rbTree.rbInsert(new Node(31)); rbTree.rbInsert(new Node(12)); rbTree.rbInsert(new Node(19)); rbTree.rbInsert(new Node(8)); // rbTree.printTree(); rbTree.rbDelete(19); rbTree.printTree(); } private final Node NIL = new Node(null, null, null, Color.BLACK, -1); private Node root; public RBTree() { root = NIL; } public RBTree(Node root) { this.root = root; } // 插入节点 public void rbInsert(Node node) { Node previous = NIL; Node temp = root; while (temp != NIL) { previous = temp; if (temp.getValue() < node.getValue()) { temp = temp.getRight(); } else { temp = temp.getLeft(); } } node.setParent(previous); if (previous == NIL) { root = node; root.setParent(NIL); } else if (previous.getValue() > node.getValue()) { previous.setLeft(node); } else { previous.setRight(node); } node.setLeft(NIL); node.setRight(NIL); node.setColor(Color.RED); rb_Insert_Fixup(node); } // 插入节点后的调整 private void rb_Insert_Fixup(Node node) { while (node.getParent().getColor() == Color.RED) { if (node.getParent() == node.getParent().getParent().getLeft()) { Node rightNuncle = node.getParent().getParent().getRight(); if (rightNuncle.getColor() == Color.RED) { // Case 1 rightNuncle.setColor(Color.BLACK); node.getParent().setColor(Color.BLACK); node.getParent().getParent().setColor(Color.RED); node = node.getParent().getParent(); } else if (node == node.getParent().getRight()) { // case 2 node = node.getParent(); leftRotate(node); } else { // case 3 node.getParent().setColor(Color.BLACK); node.getParent().getParent().setColor(Color.RED); rightRotate(node.getParent().getParent()); } } else { Node leftNuncle = node.getParent().getParent().getLeft(); if (leftNuncle.getColor() == Color.RED) { // case 4 leftNuncle.setColor(Color.BLACK); node.getParent().setColor(Color.BLACK); node.getParent().getParent().setColor(Color.RED); node = node.getParent().getParent(); } else if (node == node.getParent().getLeft()) { // case 5 node = node.getParent(); rightRotate(node); } else { // case 6 node.getParent().setColor(Color.BLACK); node.getParent().getParent().setColor(Color.RED); leftRotate(node.getParent().getParent()); } } } root.setColor(Color.BLACK); } // 删除节点 public Node rbDelete(int data) { Node node = search(data); Node temp = NIL; Node child = NIL; if (node == null) { return null; } else { if (node.getLeft() == NIL || node.getRight() == NIL) { temp = node; } else { temp = successor(node); } if (temp.getLeft() != NIL) { child = temp.getLeft(); } else { child = temp.getRight(); } child.setParent(temp.getParent()); if (temp.getParent() == NIL) { root = child; } else if (temp == temp.getParent().getLeft()) { temp.getParent().setLeft(child); } else { temp.getParent().setRight(child); } if (temp != node) { node.setValue(temp.getValue()); } if (temp.getColor() == Color.BLACK) { rb_Delete_Fixup(child); } return temp; } } // 删除节点后的调整 private void rb_Delete_Fixup(Node node) { while (node != root && node.getColor() == Color.BLACK) { if (node == node.getParent().getLeft()) { Node rightBrother = node.getParent().getRight(); if (rightBrother.getColor() == Color.RED) { // case 1 // node节点为左孩子,node节点的兄弟为RED rightBrother.setColor(Color.BLACK); node.getParent().setColor(Color.RED); leftRotate(node.getParent()); rightBrother = node.getParent().getRight(); } if (rightBrother.getLeft().getColor() == Color.BLACK && rightBrother.getRight().getColor() == Color.BLACK) { rightBrother.setColor(Color.RED); node = node.getParent(); } else if (rightBrother.getRight().getColor() == Color.BLACK) { rightBrother.getLeft().setColor(Color.BLACK); rightBrother.setColor(Color.RED); rightRotate(rightBrother); rightBrother = node.getParent().getRight(); } else { rightBrother.setColor(node.getParent().getColor()); node.getParent().setColor(Color.BLACK); rightBrother.getRight().setColor(Color.BLACK); leftRotate(node.getParent()); node = root; } } else { Node leftBrother = node.getParent().getLeft(); if (leftBrother.getColor() == Color.RED) { leftBrother.setColor(Color.BLACK); node.getParent().setColor(Color.RED); rightRotate(node.getParent()); leftBrother = node.getParent().getLeft(); } if (leftBrother.getLeft().getColor() == Color.BLACK && leftBrother.getRight().getColor() == Color.BLACK) { leftBrother.setColor(Color.RED); node = node.getParent(); } else if (leftBrother.getLeft().getColor() == Color.BLACK) { leftBrother.setColor(Color.RED); leftBrother.getRight().setColor(Color.BLACK); leftRotate(leftBrother); leftBrother = node.getParent().getLeft(); } else { leftBrother.setColor(node.getParent().getColor()); node.getParent().setColor(Color.BLACK); leftBrother.getLeft().setColor(Color.BLACK); rightRotate(node.getParent()); node = root; } } } node.setColor(Color.BLACK); } // 查找节点node的后继节点 public Node successor(Node node) { Node rightChild = node.getRight(); if (rightChild != NIL) { Node previous = null; while (rightChild != NIL) { previous = rightChild; rightChild = rightChild.getLeft(); } return previous; } else { Node parent = node.getParent(); while (parent != NIL && node != parent.getLeft()) { node = parent; parent = parent.getParent(); } return parent; } } // 查找节点 public Node search(int data) { Node temp = root; while (temp != NIL) { if (temp.getValue() == data) { return temp; } else if (data < temp.getValue()) { temp = temp.getLeft(); } else { temp = temp.getRight(); } } return null; } // 左转函数 private void leftRotate(Node node) { Node rightNode = node.getRight(); node.setRight(rightNode.getLeft()); if (rightNode.getLeft() != NIL) { rightNode.getLeft().setParent(node); } rightNode.setParent(node.getParent()); if (node.getParent() == NIL) { rightNode = root; } else if (node == node.getParent().getLeft()) { node.getParent().setLeft(rightNode); } else { node.getParent().setRight(rightNode); } rightNode.setLeft(node); node.setParent(rightNode); } // 右转函数 private void rightRotate(Node node) { Node leftNode = node.getLeft(); node.setLeft(leftNode.getRight()); if (leftNode.getRight() != null) { leftNode.getRight().setParent(node); } leftNode.setParent(node.getParent()); if (node.getParent() == NIL) { root = leftNode; } else if (node == node.getParent().getLeft()) { node.getParent().setLeft(leftNode); } else { node.getParent().setRight(leftNode); } leftNode.setRight(node); node.setParent(leftNode); } // 中序遍历红黑树 public void printTree() { inOrderTraverse(root); } private void inOrderTraverse(Node node) { if (node != NIL) { inOrderTraverse(node.getLeft()); System.out.println(" 节点:" + node.getValue() + "的颜色为:" + node.getColor()); inOrderTraverse(node.getRight()); } } public Node getNIL() { return NIL; } } class Node { private Node left; private Node right; private Node parent; private Color color; private int value; public Node(Node left, Node right, Node parent, Color color, int value) { super(); this.left = left; this.right = right; this.parent = parent; this.color = color; this.value = value; } public Node() { } public Node(int value) { this(null, null, null, null, value); } public Node getLeft() { return left; } public void setLeft(Node left) { this.left = left; } public Node getRight() { return right; } public void setRight(Node right) { this.right = right; } public Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } enum Color { RED, BLACK }