From ad1bc62a7c89166b70d0b14e06c1b1f6eae8e8e2 Mon Sep 17 00:00:00 2001 From: Mark Beck <> Date: Tue, 18 Nov 2025 09:29:05 +0100 Subject: [PATCH] feat: change to array of next nodes --- .../java/de/hs_mannheim/pr2/Skiplist.java | 153 +++++++----------- .../java/de/hs_mannheim/pr2/SkiplistTest.java | 26 ++- 2 files changed, 82 insertions(+), 97 deletions(-) diff --git a/src/main/java/de/hs_mannheim/pr2/Skiplist.java b/src/main/java/de/hs_mannheim/pr2/Skiplist.java index 4ea28ef..f10c153 100644 --- a/src/main/java/de/hs_mannheim/pr2/Skiplist.java +++ b/src/main/java/de/hs_mannheim/pr2/Skiplist.java @@ -1,12 +1,12 @@ package de.hs_mannheim.pr2; +import java.lang.reflect.Array; import java.util.Comparator; import java.util.Random; -/** - * Hello world! - */ public class Skiplist { + private static final double PROBABILITY = 0.5; + private static final int MAX_LEVEL = 32; Node head = null; int size = 0; @@ -36,121 +36,89 @@ public class Skiplist { return size == 0; } - private void insertAtLvl(int lvl, Node oldNode) { - Node newNode = new Node(oldNode.value); - newNode.down = oldNode; - - if (this.lvl < lvl) { - throw new RuntimeException(); - } else if (this.lvl == lvl) { - Node newHead = new Node(null, true); - newHead.down = this.head; - this.head = newHead; - this.lvl += 1; - } - - if (rng.nextDouble() < 0.5) { - insertAtLvl(lvl + 1, newNode); - } - - Node currentNode = this.head; - for (int i = 0; i < this.lvl - lvl - 1; i++) { - currentNode = currentNode.down; - } - - while (true) { - if (currentNode.next == null || cpr(currentNode.next.value, newNode.value) > 0) { - newNode.next = currentNode.next; - currentNode.next = newNode; - return; - } - currentNode = currentNode.next; + private int randomLevel() { + int level = 1; + while (rng.nextDouble() < PROBABILITY && level < MAX_LEVEL) { + level++; } + return level; } public void add(T data) { - Node node = new Node(data); + int level = randomLevel(); + Node node = new Node(data, level); Node currentNode = head; int counter = 0; - while (true) { - if (currentNode.next == null || cpr(currentNode.next.value, data) > 0) { - if (currentNode.down == null) { - node.next = currentNode.next; - currentNode.next = node; - break; - } else { - currentNode = currentNode.down; - } - } else { - currentNode = currentNode.next; + + for (; level > 0; level--) { + while (currentNode.next[level-1] != null && cpr(currentNode.next[level-1].value, data) < 0) { + currentNode = currentNode.next[level-1]; + counter++; } - counter++; + node.next[level-1] = currentNode.next[level-1]; + currentNode.next[level-1] = node; } size += 1; - if (rng.nextDouble() < 0.5) { - insertAtLvl(1, node); - } System.out.println("adding took " + counter + " steps"); } - private void removeUpper(int lvl, Node node) { - Node currentNode = this.head; - for (int i = 0; i < this.lvl - lvl - 1; i++) { - currentNode = currentNode.down; - } - while (currentNode.next != null) { - if (currentNode.next.down == node) { - removeUpper(lvl + 1, currentNode.next); - currentNode.next = currentNode.next.next; - return; - } - currentNode = currentNode.next; - } + private void remove(Node node) { + int level = node.next.length; + Node currentNode = head; + int counter = 0; + for (; level > 0; level--) { + while (currentNode.next[level-1] != null) { + if (currentNode.next[level-1] == node) { + currentNode.next[level-1] = node.next[level-1]; + break; + } + currentNode = currentNode.next[level-1]; + counter++; + } + } + size -= 1; + System.out.println("removing took " + counter + " steps"); } public T pop() { if (size == 0) { return null; } - Node head = this.head; - while (head.down != null) { - head = head.down; - } - removeUpper(1, head.next); - T result = head.next.value; - head.next = head.next.next; - size -= 1; - return result; + + Node node = this.head.next[0]; + remove(node); + return node.value; } - public T get(int location) { - if (location >= size) { - return null; - } + public boolean contains(T data) { Node currentNode = head; - while (currentNode.down != null) { - currentNode = currentNode.down; - } - currentNode = currentNode.next; + int counter = 0; - for (int i = 0; i < location; i++) { - currentNode = currentNode.next; + for (int level = MAX_LEVEL; level > 0; level--) { + while (currentNode.next[level-1] != null && cpr(currentNode.next[level-1].value, data) <= 0) { + if (currentNode.next[level-1].value.equals(data)) { + System.out.println("looking for took " + counter + " steps"); + return true; + } + currentNode = currentNode.next[level-1]; + counter++; + } } - return currentNode.value; + System.out.println("looking for took " + counter + " steps"); + return false; } @Override public String toString() { - Node currentHead = head; StringBuilder result = new StringBuilder(); - while (currentHead != null) { - Node currentNode = currentHead; - while (currentNode != null) { - result.append(currentNode); - currentNode = currentNode.next; + Node node = this.head; + for (int i = 0; i <= size; i++) { + result.append(String.format("%5s |", node.toString())); + for (int j = 0; j < node.next.length; j++) { + result.append("#"); } - currentHead = currentHead.down; + node = node.next[0]; result.append("\n"); } return result.toString(); @@ -162,17 +130,20 @@ public class Skiplist { // } private class Node { - Node next = null; - Node down = null; + Node[] next; T value; boolean dummy = false; - Node(T value) { + @SuppressWarnings("unchecked") + Node(T value, int level) { this.value = value; + this.next = (Node[]) Array.newInstance(Node.class, level); } + @SuppressWarnings("unchecked") Node(T value, Boolean dummy) { this.dummy = true; + this.next = (Node[]) Array.newInstance(Node.class, MAX_LEVEL); } @Override diff --git a/src/test/java/de/hs_mannheim/pr2/SkiplistTest.java b/src/test/java/de/hs_mannheim/pr2/SkiplistTest.java index 5978e57..7b720ad 100644 --- a/src/test/java/de/hs_mannheim/pr2/SkiplistTest.java +++ b/src/test/java/de/hs_mannheim/pr2/SkiplistTest.java @@ -1,6 +1,7 @@ package de.hs_mannheim.pr2; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -63,15 +64,28 @@ public class SkiplistTest { } @Test - public void canGet() { + public void canContains() { Skiplist list = new Skiplist(); list.add(1); - list.add(2); list.add(3); - assertEquals(1, list.get(0).intValue()); - assertEquals(2, list.get(1).intValue()); - assertEquals(3, list.get(2).intValue()); - assertEquals(null, list.get(3)); + list.add(2); + list.add(4); + list.add(10); + list.add(7); + list.add(8); + list.add(6); + list.add(3); + assertTrue(list.contains(1)); + assertTrue(list.contains(2)); + assertTrue(list.contains(3)); + assertTrue(list.contains(4)); + assertTrue(list.contains(6)); + assertTrue(list.contains(7)); + assertTrue(list.contains(8)); + assertTrue(list.contains(10)); + assertFalse(list.contains(5)); + assertFalse(list.contains(9)); + assertFalse(list.contains(11)); }