uebung_03: implement Task 3 (BST) and Task 4 (IntHashSet)
Task 3 adds IntBinarySearchTree with iterative add/contains and a test class covering empty trees, duplicates, and degenerate ascending and descending insertion orders. Task 4 adds IntHashSet backed by an IntLinkedList bucket array with a 0.7 load factor, Math.floorMod-based hashing for negative-int safety, doubling resize that rehashes via a private addWithoutResize helper, and a test class covering negatives, Integer.MIN_VALUE, forced collisions on bucket 0, and 100-element inserts spanning three resizes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
54
uebung_03/src/IntBinarySearchTree.java
Normal file
54
uebung_03/src/IntBinarySearchTree.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
public class IntBinarySearchTree {
|
||||||
|
IntTreeNode root;
|
||||||
|
|
||||||
|
public IntBinarySearchTree(){
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int value){
|
||||||
|
if (root == null){
|
||||||
|
root = new IntTreeNode(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntTreeNode currentRelativRoot = root;
|
||||||
|
while (true) {
|
||||||
|
if(value == currentRelativRoot.value){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value < currentRelativRoot.value) {
|
||||||
|
if (currentRelativRoot.left == null) {
|
||||||
|
currentRelativRoot.left = new IntTreeNode(value);
|
||||||
|
return;
|
||||||
|
} else currentRelativRoot = currentRelativRoot.left;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (currentRelativRoot.right == null) {
|
||||||
|
currentRelativRoot.right = new IntTreeNode(value);
|
||||||
|
return;
|
||||||
|
} else currentRelativRoot = currentRelativRoot.right;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public boolean contains(int value){
|
||||||
|
if (root == null) return false;
|
||||||
|
IntTreeNode currentRelativRoot = root;
|
||||||
|
while (true){
|
||||||
|
if(value == currentRelativRoot.value){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value < currentRelativRoot.value) {
|
||||||
|
if (currentRelativRoot.left == null) {
|
||||||
|
return false;
|
||||||
|
} else currentRelativRoot = currentRelativRoot.left;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (currentRelativRoot.right == null) {
|
||||||
|
return false;
|
||||||
|
} else currentRelativRoot = currentRelativRoot.right;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
96
uebung_03/src/IntBinarySearchTreeTest.java
Normal file
96
uebung_03/src/IntBinarySearchTreeTest.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
public class IntBinarySearchTreeTest {
|
||||||
|
|
||||||
|
static int passed = 0;
|
||||||
|
static int failed = 0;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
testEmptyContains();
|
||||||
|
testSingleAdd();
|
||||||
|
testStandardTree();
|
||||||
|
testDuplicate();
|
||||||
|
testLeftAndRightExtremes();
|
||||||
|
testStrictlyAscending();
|
||||||
|
testStrictlyDescending();
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Passed: " + passed + " / " + (passed + failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEmptyContains() {
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
checkBool("empty contains(5)", t.contains(5), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSingleAdd() {
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
t.add(42);
|
||||||
|
checkBool("single contains(42)", t.contains(42), true);
|
||||||
|
checkBool("single contains(7)", t.contains(7), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testStandardTree() {
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
int[] vals = {5, 3, 8, 1, 4, 7, 9, 5};
|
||||||
|
for (int v : vals) t.add(v);
|
||||||
|
checkBool("contains(7)", t.contains(7), true);
|
||||||
|
checkBool("contains(6)", t.contains(6), false);
|
||||||
|
checkBool("contains(1)", t.contains(1), true);
|
||||||
|
checkBool("contains(9)", t.contains(9), true);
|
||||||
|
checkBool("contains(5)", t.contains(5), true);
|
||||||
|
checkBool("contains(4)", t.contains(4), true);
|
||||||
|
checkBool("contains(3)", t.contains(3), true);
|
||||||
|
checkBool("contains(0)", t.contains(0), false);
|
||||||
|
checkBool("contains(10)", t.contains(10), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testDuplicate() {
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
t.add(10);
|
||||||
|
t.add(10);
|
||||||
|
t.add(10);
|
||||||
|
checkBool("duplicates contains(10)", t.contains(10), true);
|
||||||
|
// Tree should still work after duplicates
|
||||||
|
t.add(5);
|
||||||
|
t.add(15);
|
||||||
|
checkBool("after dup contains(5)", t.contains(5), true);
|
||||||
|
checkBool("after dup contains(15)", t.contains(15), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testLeftAndRightExtremes() {
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
int[] vals = {50, 25, 75, 10, 35, 60, 90, 5, 15, 30, 40};
|
||||||
|
for (int v : vals) t.add(v);
|
||||||
|
for (int v : vals) checkBool("extremes contains(" + v + ")", t.contains(v), true);
|
||||||
|
checkBool("extremes contains(-1)", t.contains(-1), false);
|
||||||
|
checkBool("extremes contains(100)", t.contains(100), false);
|
||||||
|
checkBool("extremes contains(45)", t.contains(45), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testStrictlyAscending() {
|
||||||
|
// Degenerates into a right-only chain - good edge case
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
for (int i = 1; i <= 10; i++) t.add(i);
|
||||||
|
for (int i = 1; i <= 10; i++) checkBool("asc contains(" + i + ")", t.contains(i), true);
|
||||||
|
checkBool("asc contains(0)", t.contains(0), false);
|
||||||
|
checkBool("asc contains(11)", t.contains(11), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testStrictlyDescending() {
|
||||||
|
// Degenerates into a left-only chain
|
||||||
|
IntBinarySearchTree t = new IntBinarySearchTree();
|
||||||
|
for (int i = 10; i >= 1; i--) t.add(i);
|
||||||
|
for (int i = 1; i <= 10; i++) checkBool("desc contains(" + i + ")", t.contains(i), true);
|
||||||
|
checkBool("desc contains(0)", t.contains(0), false);
|
||||||
|
checkBool("desc contains(11)", t.contains(11), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkBool(String label, boolean actual, boolean expected) {
|
||||||
|
if (actual == expected) {
|
||||||
|
System.out.println("PASS " + label + " = " + actual);
|
||||||
|
passed++;
|
||||||
|
} else {
|
||||||
|
System.out.println("FAIL " + label + " expected=" + expected + " actual=" + actual);
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
uebung_03/src/IntHashSet.java
Normal file
81
uebung_03/src/IntHashSet.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
public class IntHashSet {
|
||||||
|
private IntLinkedList[] buckets;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
private int capacity;
|
||||||
|
|
||||||
|
private static final double LOAD_PERCENT = 0.7;
|
||||||
|
|
||||||
|
public IntHashSet(){
|
||||||
|
buckets = new IntLinkedList[16];
|
||||||
|
capacity = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int hash(int value){
|
||||||
|
return Math.floorMod(value, capacity);
|
||||||
|
}
|
||||||
|
private boolean loadReached(){
|
||||||
|
return (double) size / capacity >= LOAD_PERCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resize(){
|
||||||
|
IntLinkedList[] oldBuckets = buckets;
|
||||||
|
capacity *=2;
|
||||||
|
buckets = new IntLinkedList[capacity];
|
||||||
|
size = 0;
|
||||||
|
for (IntLinkedList oldBucket : oldBuckets) {
|
||||||
|
if (oldBucket == null) continue;
|
||||||
|
for (int j = 0; j < oldBucket.getSize(); j++) {
|
||||||
|
addWithoutResize(oldBucket.get(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void add(int value){
|
||||||
|
int index = hash(value);
|
||||||
|
if (buckets[index] == null){
|
||||||
|
buckets[index] = new IntLinkedList();
|
||||||
|
buckets[index].add(value);
|
||||||
|
size++;
|
||||||
|
if (loadReached()) resize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < buckets[index].getSize(); i++){
|
||||||
|
if (buckets[index].get(i) == value){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buckets[index].add(value);
|
||||||
|
size++;
|
||||||
|
if (loadReached()) resize();
|
||||||
|
}
|
||||||
|
private void addWithoutResize(int value){
|
||||||
|
int index = hash(value);
|
||||||
|
if (buckets[index] == null){
|
||||||
|
buckets[index] = new IntLinkedList();
|
||||||
|
buckets[index].add(value);
|
||||||
|
size++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < buckets[index].getSize(); i++){
|
||||||
|
if (buckets[index].get(i) == value){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buckets[index].add(value);
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
public boolean contains(int value){
|
||||||
|
int index = hash(value);
|
||||||
|
if (buckets[index] == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < buckets[index].getSize(); i++){
|
||||||
|
if (buckets[index].get(i) == value){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
uebung_03/src/IntHashSetTest.java
Normal file
74
uebung_03/src/IntHashSetTest.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
public class IntHashSetTest {
|
||||||
|
static int passed = 0, failed = 0;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
testEmpty();
|
||||||
|
testAddContains();
|
||||||
|
testDuplicates();
|
||||||
|
testNegatives();
|
||||||
|
testManyForcingResize();
|
||||||
|
testCollisions();
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Passed: " + passed + " / " + (passed + failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEmpty() {
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
checkBool("empty contains(5)", s.contains(5), false);
|
||||||
|
checkBool("empty contains(-1)", s.contains(-1), false);
|
||||||
|
checkBool("empty contains(0)", s.contains(0), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAddContains() {
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
s.add(1); s.add(2); s.add(3);
|
||||||
|
checkBool("contains(1)", s.contains(1), true);
|
||||||
|
checkBool("contains(2)", s.contains(2), true);
|
||||||
|
checkBool("contains(3)", s.contains(3), true);
|
||||||
|
checkBool("contains(4)", s.contains(4), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testDuplicates() {
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
s.add(7); s.add(7); s.add(7); s.add(7);
|
||||||
|
checkBool("dup contains(7)", s.contains(7), true);
|
||||||
|
// no easy way to inspect size without exposing it; trust it
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testNegatives() {
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
s.add(-1); s.add(-100); s.add(Integer.MIN_VALUE);
|
||||||
|
checkBool("contains(-1)", s.contains(-1), true);
|
||||||
|
checkBool("contains(-100)", s.contains(-100), true);
|
||||||
|
checkBool("contains(MIN_VALUE)", s.contains(Integer.MIN_VALUE), true);
|
||||||
|
checkBool("contains(1) after negs", s.contains(1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testManyForcingResize() {
|
||||||
|
// Insert 100 values - capacity starts at 16, will resize multiple times
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
for (int i = 0; i < 100; i++) s.add(i);
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
checkBool("post-resize contains(" + i + ")", s.contains(i), true);
|
||||||
|
}
|
||||||
|
checkBool("post-resize contains(100)", s.contains(100), false);
|
||||||
|
checkBool("post-resize contains(-1)", s.contains(-1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCollisions() {
|
||||||
|
// Values that all hash to bucket 0 in capacity 16 (multiples of 16)
|
||||||
|
IntHashSet s = new IntHashSet();
|
||||||
|
s.add(0); s.add(16); s.add(32); s.add(48);
|
||||||
|
checkBool("collision contains(0)", s.contains(0), true);
|
||||||
|
checkBool("collision contains(16)", s.contains(16), true);
|
||||||
|
checkBool("collision contains(32)", s.contains(32), true);
|
||||||
|
checkBool("collision contains(48)", s.contains(48), true);
|
||||||
|
checkBool("collision contains(8)", s.contains(8), false);
|
||||||
|
checkBool("collision contains(64)", s.contains(64), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkBool(String label, boolean actual, boolean expected) {
|
||||||
|
if (actual == expected) { System.out.println("PASS " + label + " = " + actual); passed++; }
|
||||||
|
else { System.out.println("FAIL " + label + " expected=" + expected + " actual=" + actual); failed++; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
uebung_03/src/IntTreeNode.java
Normal file
12
uebung_03/src/IntTreeNode.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
public class IntTreeNode {
|
||||||
|
int value;
|
||||||
|
IntTreeNode left;
|
||||||
|
|
||||||
|
IntTreeNode right;
|
||||||
|
|
||||||
|
public IntTreeNode(int value){
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user