欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Data Structure - Binomial Queue (Java)

程序员文章站 2024-03-18 08:45:58
...
package chimomo.learning.java.datastructure;

/**
 * Implements a binomial queue.
 * Note that all "matching" is based on the compareTo method.
 *
 * @author Created by Chimomo
 */
public final class BinomialQueue<T extends Comparable<? super T>> {
    private static final int DEFAULT_TREES = 1;

    // # items in priority queue.
    private int size;

    // An array of tree roots.
    private BinomialNode<T>[] trees;

    /**
     * Construct the binomial queue.
     */
    public BinomialQueue() {
        trees = new BinomialNode[DEFAULT_TREES];
        makeEmpty();
    }

    /**
     * Construct with a single item.
     */
    public BinomialQueue(T item) {
        size = 1;
        trees = new BinomialNode[1];
        trees[0] = new BinomialNode<>(item, null, null);
    }

    /**
     * Test program.
     *
     * @param args The arguments.
     */
    public static void main(String[] args) throws Exception {
        // Construct binomial queue.
        int numItems = 10000;
        BinomialQueue<Integer> bq1 = new BinomialQueue<>();
        BinomialQueue<Integer> bq2 = new BinomialQueue<>();
        int i;
        System.out.println("Starting check.");

        // Insert.
        for (i = 37; i != 0; i = (i + 37) % numItems) {
            if (i % 2 == 0) {
                bq2.insert(i);
            } else {
                bq1.insert(i);
            }
        }

        // Merge.
        bq1.merge(bq2);

        // Delete min.
        for (i = 1; i < numItems; i++) {
            if (bq1.deleteMin() != i) {
                System.out.println("Oops! " + i);
            }
        }
        System.out.println("Check done.");
    }

    /**
     * Expand trees.
     *
     * @param newNumTrees The new number of trees.
     */
    private void expandTrees(int newNumTrees) {
        BinomialNode<T>[] old = trees;
        int oldNumTrees = trees.length;

        trees = new BinomialNode[newNumTrees];
        for (int i = 0; i < Math.min(oldNumTrees, newNumTrees); i++) {
            trees[i] = old[i];
        }
        for (int i = oldNumTrees; i < newNumTrees; i++) {
            trees[i] = null;
        }
    }

    /**
     * Merge binomial queue into the priority queue.
     * Binomial queue becomes empty.
     * Binomial queue must be different from this.
     *
     * @param binomialQueue The other binomial queue.
     */
    public void merge(BinomialQueue<T> binomialQueue) {
        // Avoid aliasing problems.
        if (this == binomialQueue) {
            return;
        }

        size += binomialQueue.size;
        if (size > capacity()) {
            int newNumTrees = Math.max(trees.length, binomialQueue.trees.length) + 1;
            expandTrees(newNumTrees);
        }

        BinomialNode<T> carry = null;
        for (int i = 0, j = 1; j <= size; i++, j *= 2) {
            BinomialNode<T> t1 = trees[i];
            BinomialNode<T> t2 = i < binomialQueue.trees.length ? binomialQueue.trees[i] : null;

            int whichCase = t1 == null ? 0 : 1;
            whichCase += t2 == null ? 0 : 2;
            whichCase += carry == null ? 0 : 4;

            switch (whichCase) {
                case 0: /* No trees */
                case 1: /* Only this */
                    break;
                case 2: /* Only binomialQueue */
                    trees[i] = t2;
                    binomialQueue.trees[i] = null;
                    break;
                case 4: /* Only carry */
                    trees[i] = carry;
                    carry = null;
                    break;
                case 3: /* This and binomialQueue */
                    carry = combineTrees(t1, t2);
                    trees[i] = binomialQueue.trees[i] = null;
                    break;
                case 5: /* This and carry */
                    carry = combineTrees(t1, carry);
                    trees[i] = null;
                    break;
                case 6: /* BinomialQueue and carry */
                    carry = combineTrees(t2, carry);
                    binomialQueue.trees[i] = null;
                    break;
                case 7: /* All three */
                    trees[i] = carry;
                    carry = combineTrees(t1, t2);
                    binomialQueue.trees[i] = null;
                    break;
            }
        }

        for (int k = 0; k < binomialQueue.trees.length; k++) {
            binomialQueue.trees[k] = null;
        }
        binomialQueue.size = 0;
    }

    /**
     * Return the result of merging equal-sized tree1 and tree2.
     */
    private BinomialNode<T> combineTrees(BinomialNode<T> tree1, BinomialNode<T> tree2) {
        if (tree1.element.compareTo(tree2.element) > 0) {
            return combineTrees(tree2, tree1);
        }
        tree2.nextSibling = tree1.leftChild;
        tree1.leftChild = tree2;
        return tree1;
    }

    /**
     * Insert into the priority queue, maintaining heap order.
     * This implementation is not optimized for O(1) performance.
     *
     * @param x The item to insert.
     */
    public void insert(T x) {
        merge(new BinomialQueue<>(x));
    }

    /**
     * Find the smallest item in the priority queue.
     *
     * @return The smallest item, or throw exception if empty.
     */
    public T findMin() throws Exception {
        if (isEmpty()) {
            throw new Exception("Binomial queue is empty!");
        }

        return trees[findMinIndex()].element;
    }

    /**
     * Find index of tree containing the smallest item in the priority queue.
     * The priority queue must not be empty.
     *
     * @return The index of tree containing the smallest item.
     */
    private int findMinIndex() {
        int i;
        int minIndex;
        for (i = 0; trees[i] == null; i++) {
        }

        for (minIndex = i; i < trees.length; i++) {
            if (trees[i] != null && trees[i].element.compareTo(trees[minIndex].element) < 0) {
                minIndex = i;
            }
        }

        return minIndex;
    }

    /**
     * Remove the smallest item from the priority queue.
     *
     * @return The smallest item, or throw exception if empty.
     */
    public T deleteMin() throws Exception {
        if (isEmpty()) {
            throw new Exception("Binomial queue is empty!");
        }

        int minIndex = findMinIndex();
        T minItem = trees[minIndex].element;
        BinomialNode<T> deletedTree = trees[minIndex].leftChild;

        // Construct H''.
        BinomialQueue<T> deletedQueue = new BinomialQueue<>();
        deletedQueue.expandTrees(minIndex);
        deletedQueue.size = (1 << minIndex) - 1;
        for (int j = minIndex - 1; j >= 0; j--) {
            deletedQueue.trees[j] = deletedTree;
            deletedTree = deletedTree.nextSibling;
            deletedQueue.trees[j].nextSibling = null;
        }

        // Construct H'.
        trees[minIndex] = null;
        size -= deletedQueue.size + 1;
        merge(deletedQueue);

        return minItem;
    }

    /**
     * Test if the priority queue is logically empty.
     *
     * @return True if empty; false otherwise.
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * Make the priority queue logically empty.
     */
    public void makeEmpty() {
        size = 0;
        for (int i = 0; i < trees.length; i++) {
            trees[i] = null;
        }
    }

    /**
     * Return the capacity.
     */
    private int capacity() {
        return (1 << trees.length) - 1;
    }

    /**
     * Binomial queue node class.
     *
     * @param <AnyType> Any type.
     */
    private static class BinomialNode<AnyType> {
        AnyType element; // The data in the node.
        BinomialNode<AnyType> leftChild; // Left child.
        BinomialNode<AnyType> nextSibling; // Next sibling.

        // Constructors.
        BinomialNode(AnyType element) {
            this(element, null, null);
        }

        BinomialNode(AnyType element, BinomialNode<AnyType> leftChild, BinomialNode<AnyType> nextSibling) {
            this.element = element;
            this.leftChild = leftChild;
            this.nextSibling = nextSibling;
        }
    }
}

/*
Output:
Starting check.
Check done.

*/