Skip to content
Snippets Groups Projects
Forked from cse332-23au / p2-public
25 commits behind the upstream repository.
BinarySearchTree.java 4.37 KiB
package cse332.datastructures.trees;

import cse332.datastructures.containers.Item;
import cse332.interfaces.misc.ComparableDictionary;
import cse332.interfaces.misc.SimpleIterator;
import cse332.interfaces.worklists.WorkList;
import datastructures.worklists.ArrayStack;

import java.lang.reflect.Array;
import java.util.Iterator;

/**
 * BinarySearchTree implements the ComparableDictionary interface using a binary
 * search tree. Notice that the key type must be Comparable.
 */
public class BinarySearchTree<K extends Comparable<? super K>, V>
        extends ComparableDictionary<K, V> {
    // The root of the BST. Root is null if and only if the tree is empty.
    // This MUST be used as your root for any class that extends this
    protected BSTNode root;

    /**
     * Create an empty binary search tree.
     */
    public BinarySearchTree() {
        super();
        this.root = null;
    }

    /**
     * Inner class to represent a node in the tree. Each node includes a data of
     * type E and an integer count. The class is protected so that subclasses of
     * BinarySearchTree can access it.
     */
    public class BSTNode extends Item<K, V> {
        public BSTNode[] children; // The children of this node.

        /**
         * Create a new data node.
         *
         * @param key
         *            key with which the specified value is to be associated
         * @param value
         *            data element to be stored at this node.
         */
        @SuppressWarnings("unchecked")
        public BSTNode(K key, V value) {
            super(key, value);
            this.children = (BSTNode[]) Array.newInstance(BSTNode.class, 2);
        }
    }

    protected BSTNode find(K key, V value) {
        BSTNode prev = null;
        BSTNode current = this.root;

        int child = -1;

        while (current != null) {
            int direction = Integer.signum(key.compareTo(current.key));

            // We found the key!
            if (direction == 0) {
                return current;
            }
            else {
                // direction + 1 = {0, 2} -> {0, 1}
                child = Integer.signum(direction + 1);
                prev = current;
                current = current.children[child];
            }
        }

        // If value is not null, we need to actually add in the new value
        if (value != null) {
            current = new BSTNode(key, null);
            if (this.root == null) {
                this.root = current;
            }
            else {
                assert(child >= 0); // child should have been set in the loop
                                    // above
                prev.children[child] = current;
            }
            this.size++;
        }

        return current;
    }

    @Override
    public V find(K key) {
        if (key == null) {
            throw new IllegalArgumentException();
        }
        BSTNode result = find(key, null);
        if (result == null) {
            return null;
        }
        return result.value;
    }

    @Override
    public V insert(K key, V value) {
        if (key == null || value == null) {
            throw new IllegalArgumentException();
        }
        BSTNode current = find(key, value);
        V oldValue = current.value;
        current.value = value;
        return oldValue;
    }

    @Override
    public Iterator<Item<K, V>> iterator() {
        return new BSTIterator();
    }

    private class BSTIterator extends SimpleIterator<Item<K, V>> {
        private BSTNode current;
        private final WorkList<BSTNode> nodes;

        public BSTIterator() {
            this.nodes = new ArrayStack<BSTNode>();
            if (BinarySearchTree.this.root != null) {
                this.current = BinarySearchTree.this.root;
            }
        }

        @Override
        public boolean hasNext() {
            return this.current != null || this.nodes.hasWork();
        }

        @Override
        public Item<K, V> next() {
            // Go left until we hit null
            while (this.current != null) {
                this.nodes.add(this.current);
                this.current = this.current.children[0];
            }

            this.current = this.nodes.next();
            Item<K, V> value = new Item<K, V>(this.current);
            this.nodes.add(this.current.children[1]);
            this.current = this.nodes.next();

            return value;
        }
    }
}