diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/Node.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/Node.java new file mode 100644 index 0000000000..52192415df --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/Node.java @@ -0,0 +1,11 @@ +package com.baeldung.algorithms.skiplist; + +class Node { + int value; + Node[] forward; // array to hold references to different levels + + public Node(int value, int level) { + this.value = value; + this.forward = new Node[level + 1]; // level + 1 because level is 0-based + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/SkipList.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/SkipList.java new file mode 100644 index 0000000000..53f7a3b0c8 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/skiplist/SkipList.java @@ -0,0 +1,92 @@ +package com.baeldung.algorithms.skiplist; + + +import java.util.Random; + +public class SkipList { + private Node head; + private int maxLevel; + private int level; + private Random random; + + public SkipList() { + maxLevel = 16; // maximum number of levels + level = 0; // current level of SkipList + head = new Node(Integer.MIN_VALUE, maxLevel); + random = new Random(); + } + + public void insert(int value) { + Node[] update = new Node[maxLevel + 1]; + Node current = this.head; + + for (int i = level; i >= 0; i--) { + while (current.forward[i] != null && current.forward[i].value < value) { + current = current.forward[i]; + } + update[i] = current; + } + + current = current.forward[0]; + + if (current == null || current.value != value) { + int lvl = randomLevel(); + + if (lvl > level) { + for (int i = level + 1; i <= lvl; i++) { + update[i] = head; + } + level = lvl; + } + + Node newNode = new Node(value, lvl); + for (int i = 0; i <= lvl; i++) { + newNode.forward[i] = update[i].forward[i]; + update[i].forward[i] = newNode; + } + } + } + + public boolean search(int value) { + Node current = this.head; + for (int i = level; i >= 0; i--) { + while (current.forward[i] != null && current.forward[i].value < value) { + current = current.forward[i]; + } + } + current = current.forward[0]; + return current != null && current.value == value; + } + + public void delete(int value) { + Node[] update = new Node[maxLevel + 1]; + Node current = this.head; + + for (int i = level; i >= 0; i--) { + while (current.forward[i] != null && current.forward[i].value < value) { + current = current.forward[i]; + } + update[i] = current; + } + current = current.forward[0]; + + if (current != null && current.value == value) { + for (int i = 0; i <= level; i++) { + if (update[i].forward[i] != current) break; + update[i].forward[i] = current.forward[i]; + } + + while (level > 0 && head.forward[level] == null) { + level--; + } + } + } + + private int randomLevel() { + int lvl = 0; + while (lvl < maxLevel && random.nextDouble() < 0.5) { + lvl++; + } + return lvl; + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/skiplist/SkipListUnitTest.java b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/skiplist/SkipListUnitTest.java new file mode 100644 index 0000000000..6f8e5d042c --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/skiplist/SkipListUnitTest.java @@ -0,0 +1,34 @@ +package com.baeldung.algorithms.skiplist; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SkipListUnitTest { + + @Test + public void givenSkipList_WhenInsert_ThenSearchFound() { + SkipList skipList = new SkipList(); + + skipList.insert(3); + assertTrue(skipList.search(3), "Should find 3"); + + assertFalse(skipList.search(99), "Should not find 99"); + } + + @Test + public void givenSkipList_WhenDeleteElement_ThenRemoveFromList() { + SkipList skipList = new SkipList(); + + skipList.insert(3); + skipList.insert(7); + + skipList.delete(3); + assertFalse(skipList.search(3), "3 should have been deleted"); + + skipList.delete(99); + assertTrue(skipList.search(7), "7 should still exist"); + } + +}