Compare commits
6 commits
e4c4da8fb9
...
0a4895c538
Author | SHA1 | Date | |
---|---|---|---|
0a4895c538 | |||
479cac0393 | |||
24225d7595 | |||
2d1e65730c | |||
d04f74f992 | |||
1f5b475645 |
7 changed files with 525 additions and 1 deletions
204
app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java
Normal file
204
app/src/main/java/ch/nuth/zhaw/exbox/AVLSearchTree.java
Normal file
|
@ -0,0 +1,204 @@
|
|||
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<T extends Comparable<T>> extends SortedBinaryTree<T> {
|
||||
private boolean balanced(TreeNode<T> node) {
|
||||
// TODO Implement (6.4)
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean balanced() {
|
||||
return balanced(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calcSize(TreeNode<T> p) {
|
||||
// TODO Implement (6.2)
|
||||
return super.calcSize(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the height of node t, or 0, if null.
|
||||
*/
|
||||
private static <T extends Comparable<T>> int height(TreeNode<T> 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<T> balance(TreeNode<T> 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)
|
||||
p = rotateR(p);
|
||||
} else {
|
||||
// TODO Implement (6.2)
|
||||
p = rotateLR(p);
|
||||
}
|
||||
} else if (height(p.right) - height(p.left) == 2) {
|
||||
if (height(p.right.right) >= height(p.right.left)) {
|
||||
// TODO Implement (6.2)
|
||||
p = rotateL(p);
|
||||
} else {
|
||||
// TODO Implement (6.2)
|
||||
p = rotateRL(p);
|
||||
}
|
||||
}
|
||||
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<T> insertAt(TreeNode<T> 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<T> rep;
|
||||
private TreeNode<T> findRepAt(TreeNode<T> 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<T> removeAt(TreeNode<T> 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<T> 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 <T extends Comparable<T>> TreeNode<T> rotateR(TreeNode<T> k2) {
|
||||
TreeNode<T> 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 <T extends Comparable<T>> TreeNode<T> rotateL(TreeNode<T> k1) {
|
||||
TreeNode<T> 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 <T extends Comparable<T>> TreeNode<T> rotateLR(TreeNode<T> 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 <T extends Comparable<T>> TreeNode<T> rotateRL(TreeNode<T> k1) {
|
||||
k1.right = rotateR(k1.right);
|
||||
return rotateL(k1);
|
||||
}
|
||||
}
|
88
app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java
Normal file
88
app/src/main/java/ch/nuth/zhaw/exbox/AVLTreeTraversal.java
Normal file
|
@ -0,0 +1,88 @@
|
|||
package ch.nuth.zhaw.exbox;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
public class AVLTreeTraversal<T extends Comparable<T>> implements Traversal<T> {
|
||||
private final TreeNode<T> root;
|
||||
|
||||
public AVLTreeTraversal(TreeNode<T> root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
private void inorder(TreeNode<T> node, Visitor<T> vis) {
|
||||
if (node != null) {
|
||||
inorder(node.left, vis);
|
||||
for (T v : node.values) vis.visit(v);
|
||||
inorder(node.right, vis);
|
||||
}
|
||||
}
|
||||
|
||||
public void inorder(Visitor<T> vis) {
|
||||
inorder(root, vis);
|
||||
}
|
||||
|
||||
private void preorder(TreeNode<T> node, Visitor<T> vis) {
|
||||
if (node != null) {
|
||||
for (T v : node.values) vis.visit(v);
|
||||
preorder(node.left, vis);
|
||||
preorder(node.right, vis);
|
||||
}
|
||||
}
|
||||
|
||||
public void preorder(Visitor<T> vis) {
|
||||
preorder(root, vis);
|
||||
}
|
||||
|
||||
private void postorder(TreeNode<T> node, Visitor<T> vis) {
|
||||
if (node != null) {
|
||||
postorder(node.left, vis);
|
||||
postorder(node.right, vis);
|
||||
for (T v : node.values) vis.visit(v);
|
||||
}
|
||||
}
|
||||
|
||||
public void postorder(Visitor<T> vis) {
|
||||
postorder(root, vis);
|
||||
}
|
||||
|
||||
void levelorder(TreeNode<T> node, Visitor<T> visitor) {
|
||||
Queue<TreeNode<T>> 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<T> vis) {
|
||||
levelorder(root, vis);
|
||||
}
|
||||
|
||||
private void interval(T min, T max, Visitor<T> visitor, TreeNode<T> 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<T> v) {
|
||||
interval(min, max, v, this.root);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ch.nuth.zhaw.exbox;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class RankingAVLTreeServer implements CommandExecutor {
|
||||
|
||||
public Tree<Competitor> createTree(String rankingText) {
|
||||
SortedBinaryTree<Competitor> result = new AVLSearchTree<>();
|
||||
List<Competitor> competitors = new RankingListServer().createList(rankingText);
|
||||
|
||||
for (Competitor competitor : competitors) {
|
||||
result.add(competitor);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String createSortedText(Tree<Competitor> competitorTree) {
|
||||
AtomicInteger rank = new AtomicInteger(1);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
competitorTree.traversal().inorder(
|
||||
(competitor) -> {
|
||||
competitor.setRank(rank.getAndIncrement());
|
||||
sb.append(competitor);
|
||||
sb.append(System.lineSeparator());
|
||||
});
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String execute(String rankingList) {
|
||||
Tree<Competitor> competitorTree = createTree(rankingList);
|
||||
return "Rangliste (Tree)\n" + createSortedText(competitorTree) + "\n\n" +
|
||||
"Height: " + competitorTree.height() + "\nSize: " + competitorTree.size();
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ public class RankingTreeServer implements CommandExecutor {
|
|||
|
||||
public String execute(String rankingList) {
|
||||
Tree<Competitor> competitorTree = createTree(rankingList);
|
||||
return "Rangliste (Tree)\n" + createSortedText(competitorTree);
|
||||
return "Rangliste (Tree)\n" + createSortedText(competitorTree) + "\n\n" +
|
||||
"Height: " + competitorTree.height() + "\nSize: " + competitorTree.size();
|
||||
}
|
||||
}
|
||||
|
|
100
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java
Normal file
100
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_2_test.java
Normal file
|
@ -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<String> 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<String> v = new MyVisitor<>();
|
||||
tree.traversal().inorder(v);
|
||||
assertEquals("ABCD", v.toString(), "inorder");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreorder() {
|
||||
Visitor<String> v = new MyVisitor<>();
|
||||
tree.traversal().preorder(v);
|
||||
assertEquals("BACD", v.toString(), "preorder");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostorder() {
|
||||
Visitor<String> v = new MyVisitor<>();
|
||||
tree.traversal().postorder(v);
|
||||
assertEquals("ADCB", v.toString(), "postorder");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLevelorder() {
|
||||
Visitor<String> 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<String> 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<T> implements Visitor<T> {
|
||||
StringBuilder output;
|
||||
|
||||
MyVisitor() {
|
||||
output = new StringBuilder();
|
||||
}
|
||||
|
||||
public void visit(T s) {
|
||||
output.append(s);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return output.toString();
|
||||
}
|
||||
}
|
43
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java
Normal file
43
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_4_test.java
Normal file
|
@ -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<String> tree;
|
||||
|
||||
static class MyTree<T extends Comparable<T>> extends AVLSearchTree<T> {
|
||||
TreeNode<T> 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<String> n = tree.getRoot();
|
||||
n.right.right.right = new TreeNode<>("Z");
|
||||
assertFalse(tree.balanced(), "should not be balanced");
|
||||
|
||||
}
|
||||
}
|
50
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java
Normal file
50
app/src/test/java/ch/nuth/zhaw/exbox/ADS6_5_test.java
Normal file
|
@ -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<String> tree;
|
||||
|
||||
@Test
|
||||
public void testMixed() {
|
||||
tree = new AVLSearchTree<>();
|
||||
List<String> 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<String> v = new MyVisitor<>();
|
||||
tree.traversal().inorder(v);
|
||||
assertEquals(expected, v.toString(), "mixed");
|
||||
|
||||
assertTrue(tree.balanced(), "balanced");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue