From 1f5b4756455799a3fbc060f0e3e017d80a25bcaa Mon Sep 17 00:00:00 2001 From: Manuel Thalmann Date: Mon, 31 Oct 2022 20:00:23 +0100 Subject: [PATCH] Add files for exercise 06 --- .../ch/nuth/zhaw/exbox/AVLSearchTree.java | 200 ++++++++++++++++++ .../ch/nuth/zhaw/exbox/AVLTreeTraversal.java | 88 ++++++++ .../main/java/ch/nuth/zhaw/exbox/ExBox.java | 13 ++ .../java/ch/nuth/zhaw/exbox/ADS6_2_test.java | 100 +++++++++ .../java/ch/nuth/zhaw/exbox/ADS6_4_test.java | 43 ++++ .../java/ch/nuth/zhaw/exbox/ADS6_5_test.java | 50 +++++ 6 files changed, 494 insertions(+) create mode 100644 app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java create mode 100644 app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java create mode 100644 app/src/main/java/ch/nuth/zhaw/exbox/ExBox.java create mode 100644 app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java create mode 100644 app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java create mode 100644 app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java b/app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java new file mode 100644 index 0000000..e226342 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java @@ -0,0 +1,200 @@ +package ch.nuth.zhaw.exbox; + +/** + * Implements an AVL tree. + * Note that all "matching" is based on the compareTo method. + * @author Mark Allen Weiss + * Generic K.Rege + */ +public class AVLSearchTree> extends SortedBinaryTree { + private boolean balanced(TreeNode node) { + // TODO Implement (6.4) + return true; + } + + public boolean balanced() { + return balanced(root); + } + + @Override + protected int calcSize(TreeNode p) { + // TODO Implement (6.2) + throw new RuntimeException(); + } + + /** + * Return the height of node t, or 0, if null. + */ + private static > int height(TreeNode t) { + return t == null ? 0 : t.height; + } + + /** + * Insert into the tree; duplicates are ignored. + * @param element the item to insert. + */ + public void add(T element) { + root = insertAt(root, element); + } + + private TreeNode balance(TreeNode p) { + if (p == null) { + return null; + } else if (height(p.left) - height(p.right) == 2) { + if (height(p.left.left) >= height(p.left.right)) { + // TODO Implement (6.2) + } else { + // TODO Implement (6.2) + } + } else if (height(p.right) - height(p.left) == 2) { + if (height(p.right.right) >= height(p.right.left)) { + // TODO Implement (6.2) + } else { + // TODO Implement (6.2) + } + } + p.height = Math.max(height(p.left), height(p.right)) + 1; + return p; + } + + /** + * Internal method to insert into a subtree. + * @param element the item to insert. + * @param p the node that roots the tree. + * @return the new root. + */ + private TreeNode insertAt(TreeNode p, T element) { + if (p == null) { + p = new TreeNode<>(element); + p.height = 1; + return p; + } else { + int c = element.compareTo(p.getValue()); + if (c == 0) { + p.values.add(element); + } else if (c < 0) { + p.left = insertAt(p.left, element); + } else { + p.right = insertAt(p.right, element); + } + } + p = balance(p); + return p; + } + + // find node to replace + private TreeNode rep; + private TreeNode findRepAt(TreeNode node) { + if (node.right != null) { + node.right = findRepAt(node.right); + node = balance(node); + } else { + rep = node; + node = node.left; + } + return node; + } + + private T removed; + + // remove node + private TreeNode removeAt(TreeNode node, T x) { + if (node == null) { + return null; + } else { + if (x.compareTo(node.getValue()) == 0) { + // found + removed = node.getValue(); + if (node.values.size() > 1) { + node.values.remove(0); + return node; + } else if (node.left == null) { + node = node.right; + } else if (node.right == null) { + node = node.left; + } else { + node.left = findRepAt(node.left); + rep.left = node.left; + rep.right = node.right; + node = rep; + } + } else if (x.compareTo(node.getValue()) <= 0) { + // search left + node.left = removeAt(node.left, x); + } else { + // search right + node.right = removeAt(node.right, x); + } + // TODO Implement (6.5) + return node; + } + } + + /** + * Remove from the tree. Nothing is done if x is not found. + * @param x the item to remove. + */ + public T remove(T x) { + removed = null; + root = removeAt(root, x); + return removed; + } + + public Traversal traversal() { + return new AVLTreeTraversal<>(root); + } + + public T removeLast() { + throw new UnsupportedOperationException(); + } + + /** + * Rotate binary tree node with left child. + * For AVL trees, this is a single rotation for case 1. + * Update heights, then return new root. + */ + private static > TreeNode rotateR(TreeNode k2) { + TreeNode k1 = k2.left; + k2.left = k1.right; + k1.right = k2; + k2.height = Math.max(height(k2.left), height(k2.right)) + 1; + k1.height = Math.max(height(k1.left), k2.height) + 1; + return k1; + } + + /** + * Rotate binary tree node with right child. + * For AVL trees, this is a single rotation for case 4. + * Update heights, then return new root. + */ + private static > TreeNode rotateL(TreeNode k1) { + TreeNode k2 = k1.right; + k1.right = k2.left; + k2.left = k1; + k1.height = Math.max(height(k1.left), height(k1.right)) + 1; + k2.height = Math.max(height(k2.right), k1.height) + 1; + return k2; + } + + /** + * Double rotate binary tree node: first left child + * with its right child; then node k3 with new left child. + * For AVL trees, this is a double rotation for case 2. + * Update heights, then return new root. + */ + private static > TreeNode rotateLR(TreeNode k3) { + k3.left = rotateL(k3.left); + return rotateR(k3); + } + + /** + * Double rotate binary tree node: first right child + * with its left child; then node k1 with new right child. + * For AVL trees, this is a double rotation for case 3. + * Update heights, then return new root. + */ + private static > TreeNode rotateRL(TreeNode k1) { + k1.right = rotateR(k1.right); + return rotateL(k1); + } +} diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java b/app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java new file mode 100644 index 0000000..e5d1de8 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java @@ -0,0 +1,88 @@ +package ch.nuth.zhaw.exbox; + +import java.util.LinkedList; +import java.util.Queue; + +public class AVLTreeTraversal> implements Traversal { + private final TreeNode root; + + public AVLTreeTraversal(TreeNode root) { + this.root = root; + } + + private void inorder(TreeNode node, Visitor vis) { + if (node != null) { + inorder(node.left, vis); + for (T v : node.values) vis.visit(v); + inorder(node.right, vis); + } + } + + public void inorder(Visitor vis) { + inorder(root, vis); + } + + private void preorder(TreeNode node, Visitor vis) { + if (node != null) { + for (T v : node.values) vis.visit(v); + preorder(node.left, vis); + preorder(node.right, vis); + } + } + + public void preorder(Visitor vis) { + preorder(root, vis); + } + + private void postorder(TreeNode node, Visitor vis) { + if (node != null) { + postorder(node.left, vis); + postorder(node.right, vis); + for (T v : node.values) vis.visit(v); + } + } + + public void postorder(Visitor vis) { + postorder(root, vis); + } + + void levelorder(TreeNode node, Visitor visitor) { + Queue> q = new LinkedList<>(); + if (node != null) { + q.offer(node); + } + while (!q.isEmpty()) { + node = q.poll(); + for (T v : node.values) visitor.visit(v); + if (node.left != null) { + q.offer(node.left); + } + if (node.right != null) { + q.offer(node.right); + } + } + } + + public void levelorder(Visitor vis) { + levelorder(root, vis); + } + + private void interval(T min, T max, Visitor visitor, TreeNode node) { + if (node != null) { + if (0 > node.getValue().compareTo(min)) { + interval(min, max, visitor, node.right); + } else if (0 < node.getValue().compareTo(max)) { + interval(min, max, visitor, node.left); + } else { + for (T v : node.values) visitor.visit(v); + interval(min, max, visitor, node.left); + interval(min, max, visitor, node.right); + } + } + } + + @Override + public void interval(T min, T max, Visitor v) { + interval(min, max, v, this.root); + } +} diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/ExBox.java b/app/src/main/java/ch/nuth/zhaw/exbox/ExBox.java new file mode 100644 index 0000000..56613ed --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/ExBox.java @@ -0,0 +1,13 @@ +package ch.nuth.zhaw.exbox; + +/** + * @author K. Rege + * @version 1.0 -- Experimentierkasten + */ +public class ExBox { + public static void main(String[] args) { + ExBoxFrame f = new ExBoxFrame(); + f.setLocationRelativeTo(null); + f.setVisible(true); + } +} \ No newline at end of file diff --git a/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java new file mode 100644 index 0000000..a9d950a --- /dev/null +++ b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java @@ -0,0 +1,100 @@ +package ch.nuth.zhaw.exbox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @(#)TreeTest.java + * + * + * @author K Rege + * @version 1.00 2018/3/17 + * @version 1.01 2021/8/1 + */ +public class ADS6_2_test { + Tree tree; + + @BeforeEach + public void setUp() { + tree = new AVLSearchTree<>(); + tree.add("B"); + tree.add("A"); + tree.add("C"); + tree.add("D"); + } + + @Test + public void testInorder() { + Visitor v = new MyVisitor<>(); + tree.traversal().inorder(v); + assertEquals("ABCD", v.toString(), "inorder"); + } + + @Test + public void testPreorder() { + Visitor v = new MyVisitor<>(); + tree.traversal().preorder(v); + assertEquals("BACD", v.toString(), "preorder"); + } + + @Test + public void testPostorder() { + Visitor v = new MyVisitor<>(); + tree.traversal().postorder(v); + assertEquals("ADCB", v.toString(), "postorder"); + } + + @Test + public void testLevelorder() { + Visitor v = new MyVisitor<>(); + tree.traversal().levelorder(v); + assertEquals("BACD", v.toString(), "levelorder"); + } + + @Test + public void testInterval() { + char left = 'K'; + char right = 'O'; + + for (int i = 0; i < 200; i++) { + char c = (char) ('A' + (Math.random() * 26)); + tree.add(Character.toString(c)); + } + // get all elements with inorder + Visitor v = new MyVisitor<>(); + tree.traversal().inorder(v); + int count = 0; + String s = v.toString(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) >= left && s.charAt(i) <= right) count++; + } + // now interval + v = new MyVisitor<>(); + tree.traversal().interval(((Character)left).toString(), Character.toString(right), v); + s = v.toString(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + assertTrue(c >= left && c <= right, c + " in interval " + left + " " + right); + } + assertEquals(count, s.length(), "size"); + } +} + +class MyVisitor implements Visitor { + StringBuilder output; + + MyVisitor() { + output = new StringBuilder(); + } + + public void visit(T s) { + output.append(s); + } + + public String toString() { + return output.toString(); + } +} diff --git a/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java new file mode 100644 index 0000000..b1c1abc --- /dev/null +++ b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java @@ -0,0 +1,43 @@ +package ch.nuth.zhaw.exbox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @(#)TreeTest.java + * + * + * @author K Rege + * @version 1.00 2018/3/17 + * @version 1.01 2021/8/1 + */ +public class ADS6_4_test { + MyTree tree; + + static class MyTree> extends AVLSearchTree { + TreeNode getRoot () { + return root; + } + } + + @BeforeEach + public void setUp() { + tree = new MyTree<>(); + tree.add("B"); + tree.add("A"); + tree.add("C"); + tree.add("D"); + } + + @Test + public void testBalanced() { + assertTrue(tree.balanced(), "should be balanced"); + TreeNode n = tree.getRoot(); + n.right.right.right = new TreeNode<>("Z"); + assertFalse(tree.balanced(), "should not be balanced"); + + } +} diff --git a/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java new file mode 100644 index 0000000..daa0697 --- /dev/null +++ b/app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java @@ -0,0 +1,50 @@ +package ch.nuth.zhaw.exbox; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @(#)TreeTest.java + * + * + * @author K Rege + * @version 1.00 2018/3/17 + * @version 1.01 2021/8/1 + */ +public class ADS6_5_test { + Tree tree; + + @Test + public void testMixed() { + tree = new AVLSearchTree<>(); + List list = new LinkedList<>(); + for (int i = 0; i < 1000; i++) { + char c = (char) ('A' + (Math.random() * 26)); + int op = (int) (Math.random() * 2); + switch (op) { + case 0: + list.add(Character.toString(c)); + tree.add(Character.toString(c)); + break; + case 1: + list.remove(Character.toString(c)); + tree.remove(Character.toString(c)); + break; + } + } + assertEquals(list.size(), tree.size()); + Collections.sort(list); + String expected = String.join("", list); + Visitor v = new MyVisitor<>(); + tree.traversal().inorder(v); + assertEquals(expected, v.toString(), "mixed"); + + assertTrue(tree.balanced(), "balanced"); + } +}