package com.jwetherell.algorithms.data_structures; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.jwetherell.algorithms.data_structures.interfaces.IList; /** * A Treap is a self-balancing binary search tree that uses randomization to maintain * a low height. In this version, it is used emulate the operations of an array and linked list. * * Time Complexity: Assuming the join/merge functions have constant complexity. * add(value), add(index,value), remove(index), set(index,value), get(index) all have O(log N). * remove(value), get(value), contains(value) all have O(N). * * Space Complexity: O(N) * * Note: This implementation is 0-based, meaning that all * indices from 0 to size() - 1, inclusive, are accessible. * * http://en.wikipedia.org/wiki/Treap * * @author Justin Wetherell */ @SuppressWarnings("unchecked") public class ImplicitKeyTreap implements IList { private static final Random RANDOM = new Random(); private static int randomSeed = Integer.MAX_VALUE; // This should be at least twice the number of Nodes protected Node root = null; protected int size = 0; /** * Default constructor. */ public ImplicitKeyTreap() { } /** * Constructor with a random seed. * * @param randomSeed to use. */ public ImplicitKeyTreap(int randomSeed) { this(); ImplicitKeyTreap.randomSeed = randomSeed; } /** * {@inheritDoc} * * Note: adds to the end of the treap (rightmost node) */ @Override public boolean add(T value) { final T t = add(size, value); return (t!=null); } /** * Insert value at index * * @param index to insert value * @param value to insert */ public T add(int index, T value) { addAtIndexAndUpdate(index, value); // Check to make sure value was added final Node n = getNodeByIndex(index); if (n == null) return null; return n.value; } /** * {@inheritDoc} */ @Override public boolean remove(T value) { // See if value already exists final int idx = getIndexByValue(value); if (idx < 0) return false; final T t = removeAtIndex(idx); return (t!=null); } /** * Remove value at index * * @param index to remove value * @return value or null if not found */ public T removeAtIndex(int index) { // See if index already exists Node n = getNodeByIndex(index); if (n == null) return null; removeAtIndexAndUpdate(index); return n.value; } /** * Set value at index * * @param index to remove value * @return value or null if not found */ public T set(int index, T value) { // See if index already exists final Node n = getNodeByIndex(index); if (n == null) return null; n.value = value; return n.value; } /** * {@inheritDoc} */ @Override public boolean contains(T value) { final Node n = getNodeByValue(value); return (n!=null); } /** * Get value at index * * @param index to remove value */ public T getAtIndex(int index) { final Node n = getNodeByIndex(index); if (n == null) return null; return n.value; } /** * {@inheritDoc} */ @Override public void clear() { root = null; size = 0; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * {@inheritDoc} */ @Override public boolean validate() { if (root == null) return true; return validateNode(root); } private boolean validateNode(Node node) { final Node left = node.left; final Node right = node.right; if (left != null) { if (node.priority < left.priority) return validateNode(left); return false; } if (right != null) { if (node.priority < right.priority) return validateNode(right); return false; } return true; } /** * {@inheritDoc} */ @Override public List toList() { return (new JavaCompatibleArrayList(this)); } /** * {@inheritDoc} */ @Override public java.util.Collection toCollection() { return (new JavaCompatibleArrayList(this)); } /** * Split the treap at index * * @param index to split at * * @return Pair which contains root of both trees */ public Pair split(int index) { final Pair p = split((Node)root, index); if (p.left != null) p.left.parent = null; if (p.right != null) p.right.parent = null; return p; } public void addAtIndexAndUpdate(int index, T value) { root = insert(((Node)root), index, value); if (root == null) size = 0; else size = (((Node)root).size); } private void removeAtIndexAndUpdate(int index) { root = remove(((Node)root), index); if (root == null) size = 0; else size = (((Node)root).size); } private Node getNodeByValue(T value) { return getNodeByValue(root,value); } private Node getNodeByIndex(int index) { if (root == null) return null; final Node l = (Node)root.left; final Node r = (Node)root.right; final int leftSize = ((l!=null)?l.size:0); final int idx = leftSize; if (idx == index) { return root; } else if (index < leftSize) { return getNodeByIndex(l, idx, index); } else { return getNodeByIndex(r, idx, index); } } private int getIndexByValue(T value) { final Node node = (Node)root; if (value == null || node == null) return Integer.MIN_VALUE; final Node l = (Node)node.left; final Node r = (Node)node.right; final int leftSize = ((l!=null)?l.size:0); final int idx = leftSize; if (value.equals(node.value)) return idx; int i = getIndexByValue(l, idx, value); if (i >= 0) return i; i = getIndexByValue(r, idx, value); return i; } /** * Split the treap rooted at node at given index * * @param node which represents root * @param index in treap to split * * @return Pair which contains root of both trees */ public static Pair split(Node node, int index) { if (node == null) return new Pair(null, null); final int leftSize = (node.left!=null? ((Node)node.left).size : 0); if (index <= leftSize) { final Pair sub = split((Node)node.left, index); node.left = sub.right; if (node.left != null) node.left.parent = node; sub.right = node; node.update(); return sub; } // else final Pair sub = split((Node)node.right, (index - leftSize - 1)); node.right = sub.left; if (node.right != null) node.right.parent = node; sub.left = node; node.update(); return sub; } /** * Merge treaps from given left and right nodes * * @param left node which represents root of left treap * @param right node which represents root of great treap * * @return treap from merged treaps */ public static Node merge(Node left, Node right) { if (left == null) return right; if (right == null) return left; if (left.priority < right.priority) { left.right = merge((Node)left.right, right); if (left.right != null) left.right.parent = left; left.update(); return left; } // else right.left = merge(left, (Node)right.left); if (right.left != null) right.left.parent = right; right.update(); return right; } private static Node insert(Node root, int index, T value) { final Pair p = split(root, index); return merge(merge((Node)p.left, new Node(value)), (Node)p.right); } private static Node remove(Node root, int index) { final Pair p = split(root, index); final int leftSize = (p.left!=null? ((Node)p.left).size : 0); return merge(p.left, (split(p.right, (index + 1 - leftSize))).right); } private static Node getNodeByValue(Node node, T value) { if (node == null) return null; if (node.value.equals(value)) return node; Node n = getNodeByValue(node.left, value); if (n == null) n = getNodeByValue(node.right, value); return n; } private static Node getNodeByIndex(Node node, int parentIndex, int index) { if (node == null) return null; final Node p = (Node)node.parent; final Node l = (Node)node.left; final Node r = (Node)node.right; final int leftSize = ((l!=null)?l.size:0); final int rightSize = ((r!=null)?r.size:0); int idx = Integer.MIN_VALUE; if (p!=null && node.equals(p.left)) { // left idx = parentIndex - rightSize - 1; } else if (p!=null && node.equals(p.right)) { // right idx = leftSize + parentIndex + 1; } else { throw new RuntimeException("I do not have a parent :-("); } if (idx == index) return node; if (index <= idx) { return getNodeByIndex(l, idx, index); } else { return getNodeByIndex(r, idx, index); } } private static int getIndexByValue(Node node, int parentIndex, T value) { if (node == null) return Integer.MIN_VALUE; final Node p = (Node)node.parent; final Node l = (Node)node.left; final Node r = (Node)node.right; final int leftSize = ((l!=null)?l.size:0); final int rightSize = ((r!=null)?r.size:0); int idx = Integer.MIN_VALUE; if (p!=null && node.equals(p.left)) { // left idx = parentIndex - rightSize - 1; } else if (p!=null && node.equals(p.right)) { // right idx = leftSize + parentIndex + 1; } else { throw new RuntimeException("I do not have a parent :-("); } if (value.equals(node.value)) return idx; int i = getIndexByValue(l, idx, value); if (i >= 0) return i; i = getIndexByValue(r, idx, value); return i; } public T[] inOrder() { return inOrder(root,size); } public static T[] inOrder(Node node, int size) { T[] data = (T[]) new Object[size]; if (node == null) return data; inOrder(node, data, 0); return data; } private static int inOrder(Node node, T[] data, int idx) { if (node == null) return idx; idx = inOrder(node.left, data, idx); data[idx++] = node.value; idx = inOrder(node.right, data, idx); return idx; } /** * {@inheritDoc} */ @Override public String toString() { return TreePrinter.getString(this); } public static class Node { private T value = null; private int priority; private int size; private Node parent = null; private Node left = null; private Node right = null; private Node(T id) { this(null, id); } private Node(Node parent, T id) { this.parent = parent; this.value = id; this.size = 1; this.priority = RANDOM.nextInt(randomSeed); } public int getSize() { return size; } private void update() { size = 1 + (left!=null? ((Node)left).size : 0) + (right!=null? ((Node)right).size : 0); } /** * {@inheritDoc} */ @Override public String toString() { return "id=" + value + " parent=" + ((parent != null) ? parent.value : "NULL") + " left=" + ((left != null) ? left.value : "NULL") + " right=" + ((right != null) ? right.value : "NULL"); } } public static class Pair { private Node left; private Node right; private Pair(Node left, Node right) { this.left = left; this.right = right; } public Node getLesser() { return left; } public Node getGreater() { return right; } /** * {@inheritDoc} */ @Override public String toString() { return "left={"+left.toString()+"} right={"+right.toString()+"}"; } } public static class JavaCompatibleArrayList extends java.util.AbstractList implements java.util.RandomAccess { private ImplicitKeyTreap list = null; public JavaCompatibleArrayList(ImplicitKeyTreap list) { this.list = list; } /** * {@inheritDoc} */ @Override public boolean add(T value) { return list.add(value); } /** * {@inheritDoc} */ @Override public boolean remove(Object value) { return list.remove((T)value); } /** * {@inheritDoc} */ @Override public boolean contains(Object value) { return list.contains((T)value); } /** * {@inheritDoc} */ @Override public int size() { return list.size; } /** * {@inheritDoc} */ @Override public void add(int index, T value) { list.add(index, value); } /** * {@inheritDoc} */ @Override public T remove(int index) { return list.removeAtIndex(index); } /** * {@inheritDoc} */ @Override public T get(int index) { T t = list.getAtIndex(index); if (t!=null) return t; throw new IndexOutOfBoundsException(); } /** * {@inheritDoc} */ @Override public T set(int index, T value) { return list.set(index, value); } } protected static class TreePrinter { public static String getString(ImplicitKeyTreap tree) { if (tree.root == null) return "Tree has no nodes."; return getString(tree.root, "", true); } private static String getString(Node node, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); if (node.parent != null) { String side = "left"; if (node.equals(node.parent.right)) side = "right"; builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + side + ") " + node.value + "\n"); } else { builder.append(prefix + (isTail ? "└── " : "├── ") + node.value + "\n"); } List> children = null; if (node.left != null || node.right != null) { children = new ArrayList>(2); if (node.left != null) children.add(node.left); if (node.right != null) children.add(node.right); } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } }