Data Structure - Splay Tree (Java)
阿新 • • 發佈:2018-12-29
package chimomo.learning.java.datastructure; /** * Implements a top-down splay tree. * Note that all "matching" is based on the compareTo method. * * @author Created by Chimomo */ public class SplayTree<T extends Comparable<? super T>> { // Used between different inserts. private BinaryNode<T> newNode = null; // For splay. private BinaryNode<T> header = new BinaryNode<>(null); private BinaryNode<T> root; private BinaryNode<T> nullNode; /** * Construct the tree. */ public SplayTree() { nullNode = new BinaryNode<>(null); nullNode.left = nullNode.right = nullNode; root = nullNode; } // Test program. // Should print min and max and nothing else. public static void main(String[] args) throws Exception { // Construct splay tree. SplayTree<Integer> t = new SplayTree<>(); final int NUMS = 40000; final int GAP = 307; System.out.println("Checking... (no bad output means success)"); // Insert. for (int i = GAP; i != 0; i = (i + GAP) % NUMS) { t.insert(i); } System.out.println("Inserts complete"); // Remove. for (int i = 1; i < NUMS; i += 2) { t.remove(i); } System.out.println("Removes complete"); // Find min and find max. if (t.findMin() != 2 || t.findMax() != NUMS - 2) { System.out.println("FindMin or FindMax error!"); } // Contains. for (int i = 2; i < NUMS; i += 2) { if (!t.contains(i)) { System.out.println("Error: Find fails for " + i); } } for (int i = 1; i < NUMS; i += 2) { if (t.contains(i)) { System.out.println("Error: Found deleted item " + i); } } } /** * Rotate binary tree node with left child. * For AVL trees, this is a single rotation for case 1. */ private static <AnyType> BinaryNode<AnyType> rotateWithLeftChild(BinaryNode<AnyType> k2) { BinaryNode<AnyType> k1 = k2.left; k2.left = k1.right; k1.right = k2; return k1; } /** * Rotate binary tree node with right child. * For AVL trees, this is a single rotation for case 4. */ private static <AnyType> BinaryNode<AnyType> rotateWithRightChild(BinaryNode<AnyType> k1) { BinaryNode<AnyType> k2 = k1.right; k1.right = k2.left; k2.left = k1; return k2; } /** * Insert into the tree. * * @param x The item to insert. */ public void insert(T x) { if (newNode == null) { newNode = new BinaryNode<>(null); } newNode.element = x; if (root == nullNode) { newNode.left = newNode.right = nullNode; root = newNode; } else { root = splay(x, root); int compareResult = x.compareTo(root.element); if (compareResult < 0) { newNode.left = root.left; newNode.right = root; root.left = nullNode; root = newNode; } else if (compareResult > 0) { newNode.right = root.right; newNode.left = root; root.right = nullNode; root = newNode; } else { // No duplicates. return; } } // So next insert will call new. newNode = null; } /** * Remove from the tree. * * @param x The item to remove. */ public void remove(T x) { if (!contains(x)) { return; } BinaryNode<T> newTree; // If x is found, it will be splayed to the root by contains. if (root.left == nullNode) { newTree = root.right; } else { // Find the maximum in the left subtree. // Splay it to the root; and then attach right child. newTree = root.left; newTree = splay(x, newTree); newTree.right = root.right; } root = newTree; } /** * Find the smallest item in the tree. * Not the most efficient implementation (uses two passes), but has correct amortized behavior. * A good alternative is to first call find with parameter smaller than any item in the tree, then call findMin. * * @return The smallest item or throw exception if empty. */ public T findMin() throws Exception { if (isEmpty()) { throw new Exception("Splay tree is empty!"); } BinaryNode<T> ptr = root; while (ptr.left != nullNode) { ptr = ptr.left; } root = splay(ptr.element, root); return ptr.element; } /** * Find the largest item in the tree. * Not the most efficient implementation (uses two passes), but has correct amortized behavior. * A good alternative is to first call find with parameter larger than any item in the tree, then call findMax. * * @return The largest item or throw exception if empty. */ public T findMax() throws Exception { if (isEmpty()) { throw new Exception("Splay tree is empty!"); } BinaryNode<T> ptr = root; while (ptr.right != nullNode) { ptr = ptr.right; } root = splay(ptr.element, root); return ptr.element; } /** * Find an item in the tree. * * @param x The item to search for. * @return True if x is found; false otherwise. */ public boolean contains(T x) { if (isEmpty()) { return false; } root = splay(x, root); return root.element.compareTo(x) == 0; } /** * Make the tree logically empty. */ public void makeEmpty() { root = nullNode; } /** * Test if the tree is logically empty. * * @return True if empty, false otherwise. */ public boolean isEmpty() { return root == nullNode; } /** * Internal method to perform a top-down splay. * The last accessed node becomes the new root. * * @param x The target item to splay around. * @param t The root of the subtree to splay. * @return The subtree after the splay. */ private BinaryNode<T> splay(T x, BinaryNode<T> t) { BinaryNode<T> leftTree, rightTree; header.left = header.right = nullNode; leftTree = rightTree = header; // Guarantee a match. nullNode.element = x; for (; ; ) { int compareResult = x.compareTo(t.element); if (compareResult < 0) { if (x.compareTo(t.left.element) < 0) { t = rotateWithLeftChild(t); } if (t.left == nullNode) { break; } // Link right. rightTree.left = t; rightTree = t; t = t.left; } else if (compareResult > 0) { if (x.compareTo(t.right.element) > 0) { t = rotateWithRightChild(t); } if (t.right == nullNode) { break; } // Link left. leftTree.right = t; leftTree = t; t = t.right; } else { break; } } leftTree.right = t.left; rightTree.left = t.right; t.left = header.right; t.right = header.left; return t; } /** * Basic node stored in unbalanced binary search trees. * * @param <AnyType> Any type. */ private static class BinaryNode<AnyType> { AnyType element; // The data in the node. BinaryNode<AnyType> left; // Left child. BinaryNode<AnyType> right; // Right child. // Constructors. BinaryNode(AnyType element) { this(element, null, null); } BinaryNode(AnyType element, BinaryNode<AnyType> left, BinaryNode<AnyType> right) { this.element = element; this.left = left; this.right = right; } } } /* Output: Checking... (no bad output means success) Inserts complete Removes complete */