diff --git a/.vscode/launch.json b/.vscode/launch.json index f223193..9d83bfa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,14 +6,21 @@ "configurations": [ { "type": "java", - "name": "Main", + "name": "assn06", + "request": "launch", + "mainClass": "assn06.Main", + "projectName": "COMP210_238a5b6" + }, + { + "type": "java", + "name": "assn04", "request": "launch", "mainClass": "assn04.Main", "projectName": "COMP210_238a5b6" }, { "type": "java", - "name": "Main", + "name": "assn03", "request": "launch", "mainClass": "assn03.Main", "projectName": "COMP210_238a5b6" diff --git a/src/assn06/AVLTree.java b/src/assn06/AVLTree.java new file mode 100644 index 0000000..5e1e4da --- /dev/null +++ b/src/assn06/AVLTree.java @@ -0,0 +1,203 @@ +package assn06; + +public class AVLTree> implements SelfBalancingBST { + // Fields + private T _value; + private AVLTree _left; + private AVLTree _right; + private int _height; + private int _size; + + public AVLTree() { + _value = null; + _left = null; + _right = null; + _height = -1; + _size = 0; + } + + private void updateMeta() { + this._height = 1 + Math.max(this._left.height(), this._right.height()); + this._size = 1 + this._left.size() + this._right.size(); + } + + /** + * Rotates the tree left and returns + * AVLTree root for rotated result. + */ + private AVLTree rotateLeft() { + if (isEmpty()) + throw new RuntimeException("Illegal operation on empty tree"); + + AVLTree newRoot = this._right; + this._right = newRoot._left; + newRoot._left = this; + + newRoot._left.updateMeta(); + newRoot.updateMeta(); + + return newRoot; + } + + /** + * Rotates the tree right and returns + * AVLTree root for rotated result. + */ + private AVLTree rotateRight() { + if (isEmpty()) + throw new RuntimeException("Illegal operation on empty tree"); + + AVLTree newRoot = this._left; + this._left = newRoot._right; + newRoot._right = this; + + newRoot._right.updateMeta(); + newRoot.updateMeta(); + + return newRoot; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public int height() { + return _height; + } + + @Override + public int size() { + return _size; + } + + private int balanceFactor() { + return _left.height() - _right.height(); + } + + private SelfBalancingBST rebalance() { + AVLTree self = this; + self.updateMeta(); + + if (self.balanceFactor() > 1) + if (self._left.balanceFactor() < 0) { + self._left = self._left.rotateLeft(); + self = self.rotateRight(); + } else + self = self.rotateRight(); + else if (self.balanceFactor() < -1) + if (self._right.balanceFactor() > 0) { + self._right = self._right.rotateRight(); + self = self.rotateLeft(); + } else + self = self.rotateLeft(); + + return self; + } + + @Override + public SelfBalancingBST insert(T element) { + if (isEmpty()) { + _value = element; + _left = new AVLTree(); + _right = new AVLTree(); + _height = 0; + _size = 1; + + return this; + } + + if (_value.compareTo(element) > 0) + _left = (AVLTree) _left.insert(element); + else if (_value.compareTo(element) < 0) + _right = (AVLTree) _right.insert(element); + + return this.rebalance(); + } + + @Override + public SelfBalancingBST remove(T element) { + if (!contains(element)) + return this; + + if (_value.compareTo(element) > 0) + _left = (AVLTree) _left.remove(element); + else if (_value.compareTo(element) < 0) + _right = (AVLTree) _right.remove(element); + else { + if (_left.isEmpty() && _right.isEmpty()) + return new AVLTree(); + else if (_left.isEmpty()) + return _right; + else if (_right.isEmpty()) + return _left; + else { + _value = _right.findMin(); + _right = (AVLTree) _right.remove(_value); + } + } + + return this.rebalance(); + } + + @Override + public T findMin() { + if (isEmpty()) { + throw new RuntimeException("Illegal operation on empty tree"); + } + if (_left.isEmpty()) { + return _value; + } else { + return _left.findMin(); + } + } + + @Override + public T findMax() { + if (isEmpty()) { + throw new RuntimeException("Illegal operation on empty tree"); + } + if (_right.isEmpty()) { + return _value; + } else { + return _right.findMax(); + } + } + + @Override + public boolean contains(T element) { + if (isEmpty()) + return false; + + if (_value.compareTo(element) == 0) + return true; + else if (_value.compareTo(element) > 0) + return _left.contains(element); + else + return _right.contains(element); + + } + + @Override + public T getValue() { + return _value; + } + + @Override + public SelfBalancingBST getLeft() { + if (isEmpty()) { + return null; + } + return _left; + } + + @Override + public SelfBalancingBST getRight() { + if (isEmpty()) { + return null; + } + return _right; + } + +} diff --git a/src/assn06/Main.java b/src/assn06/Main.java new file mode 100644 index 0000000..4ee7c8c --- /dev/null +++ b/src/assn06/Main.java @@ -0,0 +1,42 @@ +package assn06; + +public class Main { + public static void main(String[] args) { + + // Create a new empty tree. + SelfBalancingBST avl_bst = new AVLTree(); + + // Insert 50 random integers. + // Note how we need to capture the result of insert back into + // the variable avl_bst because the post-insertion root that is + // returned may be different from the original root because of the insertion. + // result should be about 6. + + for (int i = 0; i < 50; i++) { + int rand = (int) (Math.random() * 100); + while (avl_bst.contains(rand)) + rand = (int) (Math.random() * 100); + + avl_bst = avl_bst.insert(rand); + } + System.out.println("\nheight: " + avl_bst.height()); + System.out.println("size: " + avl_bst.size()); + + // Now insert 50 integers in increasing order which would + // cause a simple BST to become very tall but for our + // self-balancing tree won't be too bad (should be 7) + + for (int i = 150; i < 200; i++) { + avl_bst = avl_bst.insert(i); + } + System.out.println("\nheight: " + avl_bst.height()); + System.out.println("size: " + avl_bst.size()); + + // test remove + avl_bst = avl_bst.remove(avl_bst.findMax()); + avl_bst = avl_bst.remove(avl_bst.findMin()); + + System.out.println("\nheight: " + avl_bst.height()); + System.out.println("size: " + avl_bst.size()); + } +} diff --git a/src/assn06/SelfBalancingBST.java b/src/assn06/SelfBalancingBST.java new file mode 100644 index 0000000..12db09d --- /dev/null +++ b/src/assn06/SelfBalancingBST.java @@ -0,0 +1,76 @@ +package assn06; + +public interface SelfBalancingBST> { + + /** + * @return true if the tree is empty + */ + boolean isEmpty(); + + /** + * @return height of the tree. + */ + int height(); + + /** + * @return the number of elements in the tree + */ + int size(); + + /** + * Inserts element into tree and returns resulting + * tree (i.e. root) after insertion. Depending on implementation, + * this may or may not be the same object that you started with. + * @param element to be added to the tree + * @return resulting tree after insertion + **/ + SelfBalancingBST insert(T element); + + /** + * Removes element from tree and returns resulting + * tree after removal. Depending on implementation, + * this may or may not be the same object that you + * started with. If element is not in the tree, the + * tree should remain unchanged and return itself. + * @param element to be removed from the tree + * @return resulting tree after removal + **/ + SelfBalancingBST remove(T element); + + /** + * Throws a RuntimeException if called on an empty tree. + * @return the smallest value in the tree + */ + T findMin(); + + /** + * Throws a RuntimeException if called on an empty tree. + * @return the smallest value in the tree. + */ + T findMax(); + + /** + * @param element whose presence in this tree is to be tested + * @return true if this tree contains the specified element + */ + boolean contains(T element); + + /** + * @return value at the top of the tree. + * If a tree is empty, its value is null. + */ + T getValue(); + + /** + * @return the left child of the tree. + * Throws a RuntimeException if the tree is empty. + */ + SelfBalancingBST getLeft(); + + /** + * @return the right child of the tree. + * Throws a RuntimeException if the tree is empty. + */ + SelfBalancingBST getRight(); + +}