diff --git a/src/assn06/AVLTree.java b/src/assn06/AVLTree.java new file mode 100644 index 0000000..5cf5a81 --- /dev/null +++ b/src/assn06/AVLTree.java @@ -0,0 +1,287 @@ +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; + } + + /** + * Rotates the tree left and returns + * AVLTree root for rotated result. + */ + private AVLTree rotateLeft() { + if (isEmpty() || _right == null) { + return this; + } + + AVLTree newRoot = _right; + AVLTree temp = newRoot._left; + + newRoot._left = this; + _right = temp; + + updateHeight(); + newRoot.updateHeight(); + + return newRoot; + } + + /** + * Rotates the tree right and returns + * AVLTree root for rotated result. + */ + private AVLTree rotateRight() { + if (isEmpty() || _left == null) { + return this; + } + AVLTree newRoot = _left; + AVLTree temp = newRoot._right; + + newRoot._right = this; + _left = temp; + + updateHeight(); + newRoot.updateHeight(); + + return newRoot; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public int height() { + return _height; + } + + @Override + public int size() { + return _size; + } + + @Override + public SelfBalancingBST insert(T element) { + if (isEmpty()) { + _value = element; + _left = new AVLTree<>(); + _right = new AVLTree<>(); + _height = 0; + _size = 1; + return this; + } + + int compareResult = element.compareTo(_value); + + if (compareResult < 0) { + _left = (AVLTree) _left.insert(element); + } else if (compareResult > 0) { + _right = (AVLTree) _right.insert(element); + } else { + // Duplicate values are not allowed in this AVL tree implementation + return this; + } + + updateHeight(); + _size++; + + int balance = getBalance(); + + if (balance > 1) { + if (_left.getBalance() < 0) { + _left = _left.rotateLeft(); + } + return rotateRight(); + } + + else if (balance < -1) { + if (_right.getBalance() > 0) { + _right = _right.rotateRight(); + } + return rotateLeft(); + } + + return this; + } + + + @Override + public SelfBalancingBST remove(T element) { + if (isEmpty()) { + return this; + } + + int compareResult = element.compareTo(_value); + + if (compareResult < 0) { + _left = (AVLTree) _left.remove(element); + } else if (compareResult > 0) { + _right = (AVLTree) _right.remove(element); + } else { + // Node with only one child or no child + if (_left.isEmpty() || _right.isEmpty()) { + AVLTree temp = _left.isEmpty() ? _right : _left; + if (temp.isEmpty()) { + return new AVLTree<>(); + } else { + return temp; + } + } else { + _value = _right.findMin(); + _right = (AVLTree) _right.remove(_value); + } + } + + updateHeight(); + _size--; + + int balance = getBalance(); + + if (balance > 1) { + if (_left.getBalance() < 0) { + _left = _left.rotateLeft(); + } + return rotateRight(); + } + + else if (balance < -1) { + if (_right.getBalance() > 0) { + _right = _right.rotateRight(); + } + return rotateLeft(); + } + + return this; + } + + @Override + public T findMin() { + if (isEmpty()) { + throw new RuntimeException("Illegal operation on empty tree"); + } + if (_left == null || _left.isEmpty()) { + return _value; + } else { + return _left.findMin(); + } + } + + @Override + public T findMax() { + if (isEmpty()) { + throw new RuntimeException("Illegal operation on empty tree"); + } + // Check if _right is not null before calling isEmpty on it + if (_right == null || _right.isEmpty()) { + return _value; + } else { + return _right.findMax(); + } + } + + @Override + public boolean contains(T element) { + if (isEmpty()) { + return false; + } + + if (_value.compareTo(element) > 0) { + return _left.contains(element); + } else if (_value.compareTo(element) < 0) { + return _right.contains(element); + } else { + return true; + } + } + + @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; + } + + private void updateHeight() { + int leftHeight = (_left != null) ? _left.height() : -1; + int rightHeight = (_right != null) ? _right.height() : -1; + _height = 1 + Math.max(leftHeight, rightHeight); + } + + private int getBalance() { + if (isEmpty()) { + return 0; + } + + int leftHeight = _left != null ? _left.height() : -1; + int rightHeight = _right != null ? _right.height() : -1; + + return leftHeight - rightHeight; + } + + public String toString() { + if (isEmpty()) { + return ""; + } + + String result = ""; + + if (_left != null) { + result += _left.toString(); + } + + result += _value + " "; + + if (_right != null) { + result += _right.toString(); + } + + return result; + } + + /** + * Checks if the tree is balanced. + * + * @return true if the tree is balanced, false otherwise. + */ + public boolean isBalanced() { + if (isEmpty()) { + return true; + } + + int balance = getBalance(); + if (Math.abs(balance) > 1) { + return false; + } else { + boolean leftBalanced = _left == null || _left.isBalanced(); + boolean rightBalanced = _right == null || _right.isBalanced(); + return leftBalanced && rightBalanced; + } + } + +} diff --git a/src/assn06/COMP210-F23-Assn6-v1.3.pdf b/src/assn06/COMP210-F23-Assn6-v1.3.pdf new file mode 100644 index 0000000..bb27207 Binary files /dev/null and b/src/assn06/COMP210-F23-Assn6-v1.3.pdf differ diff --git a/src/assn06/Main.java b/src/assn06/Main.java new file mode 100644 index 0000000..09af375 --- /dev/null +++ b/src/assn06/Main.java @@ -0,0 +1,44 @@ +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++) { + avl_bst = avl_bst.insert((int) (Math.random() * 100)); + } + System.out.println(avl_bst.height()); + + // 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 = 0; i < 50; i++) { + avl_bst = avl_bst.insert(i); + } + System.out.println(avl_bst.height()); + // System.out.println(avl_bst.toString()); + + System.out.println("test cases:"); + + SelfBalancingBST avl = new AVLTree(); + + int[] numbers = { 20, 11, 50, 4, 6, 15, 3, 16, 17 }; + + for (int i = 0; i < numbers.length; i++) { + avl = avl.insert(numbers[i]); + } + + System.out.println(avl.height()); + System.out.println(avl.toString()); + System.out.println(avl.isBalanced()); + } +} diff --git a/src/assn06/SelfBalancingBST.java b/src/assn06/SelfBalancingBST.java new file mode 100644 index 0000000..69c579c --- /dev/null +++ b/src/assn06/SelfBalancingBST.java @@ -0,0 +1,81 @@ +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(); + + /** + * @return true if the tree is balanced. + */ + boolean isBalanced(); + +}