feat: add Skiplist impl

main
Mark Beck 2025-11-18 07:33:31 +01:00
parent c63d0e5354
commit 17a471e3a0
8 changed files with 834 additions and 0 deletions

135
.gitignore vendored 100644
View File

@ -0,0 +1,135 @@
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,java,maven,eclipse
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,java,maven,eclipse
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project
### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,java,maven,eclipse

0
.mvn/jvm.config 100644
View File

View File

205
checkstyle.xml 100644
View File

@ -0,0 +1,205 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
http://java.sun.com/docs/books/jls/second_edition/html/index.html
- the Sun Code Conventions at http://java.sun.com/docs/codeconv/
- the Javadoc guidelines at
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
- the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
http://checkstyle.sourceforge.net/5.x/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<!-- Checks whether files end with a new line. -->
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
<!-- <module name="NewlineAtEndOfFile"/> -->
<!-- Checks that property files contain the same keys. -->
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="FileLength"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<!--
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
-->
<module name="LineLength">
<property name="ignorePattern" value="^.*//#.*|^.* \* @see .*|^import .*"/>
<property name="max" value="100"/>
</module>
<module name="TreeWalker">
<module name="SuppressionCommentFilter"/>
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="JavadocVariable">
<property name="scope" value="public"/>
</module>
<module name="JavadocStyle"/>
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName">
<property name="format" value="^[a-z][a-z0-9_]+(\.[a-z][a-z0-9_]*)*$"/>
</module>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for Headers -->
<!-- See http://checkstyle.sf.net/config_header.html -->
<!-- <module name="Header"> -->
<!-- The follow property value demonstrates the ability -->
<!-- to have access to ANT properties. In this case it uses -->
<!-- the ${basedir} property to allow Checkstyle to be run -->
<!-- from any directory within a project. See property -->
<!-- expansion, -->
<!-- http://checkstyle.sf.net/config.html#properties -->
<!-- <property -->
<!-- name="headerFile" -->
<!-- value="${basedir}/java.header"/> -->
<!-- </module> -->
<!-- Following interprets the header file as regular expressions. -->
<!-- <module name="RegexpHeader"/> -->
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="MethodLength">
<property name="countEmpty" value="false"/>
</module>
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter">
<property name="tokens" value="AT, INC, DEC, UNARY_MINUS, UNARY_PLUS, BNOT, LNOT, DOT, ARRAY_DECLARATOR, INDEX_OP"/>
</module>
<module name="NoWhitespaceBefore">
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, ELLIPSIS, LABELED_STAT"/>
</module>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<!-- <module name="RedundantModifier"/> -->
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<!-- <module name="RightCurly"/> -->
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!-- <module name="AvoidInlineConditionals"/> -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!-- <module name="DesignForExtension"/> -->
<module name="FinalClass"/>
<!-- <module name="HideUtilityClassConstructor"/> -->
<module name="InterfaceIsType"/>
<module name="VisibilityModifier">
<property name="protectedAllowed" value="true"/>
<property name="packageAllowed" value="true"/>
</module>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<!-- <module name="FinalParameters"/> -->
<module name="TodoComment"/>
<module name="UpperEll"/>
</module>
</module>

60
pmd.xml 100644
View File

@ -0,0 +1,60 @@
<?xml version="1.0"?>
<ruleset name="Custom Rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
My custom rules
</description>
<rule ref="category/java/bestpractices.xml/AccessorClassGeneration"/>
<!-- <rule ref="category/java/bestpractices.xml/AvoidMessageDigestField"/> -->
<rule ref="category/java/bestpractices.xml/AvoidReassigningCatchVariables"/>
<rule ref="category/java/bestpractices.xml/AvoidReassigningLoopVariables"/>
<rule ref="category/java/bestpractices.xml/AvoidReassigningParameters"/>
<rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>
<rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/>
<rule ref="category/java/bestpractices.xml/CheckResultSet"/>
<rule ref="category/java/bestpractices.xml/ConstantsInInterface"/>
<rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitch"/>
<rule ref="category/java/bestpractices.xml/DoubleBraceInitialization"/>
<rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/>
<rule ref="category/java/bestpractices.xml/ForLoopVariableCount"/>
<rule ref="category/java/bestpractices.xml/GuardLogStatement"/>
<rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation"/>
<rule ref="category/java/bestpractices.xml/UnitTestShouldUseAfterAnnotation"/>
<rule ref="category/java/bestpractices.xml/UnitTestShouldUseBeforeAnnotation"/>
<rule ref="category/java/bestpractices.xml/UnitTestShouldUseTestAnnotation"/>
<rule ref="category/java/bestpractices.xml/JUnit5TestShouldBePackagePrivate"/>
<!-- <rule ref="category/java/bestpractices.xml/UnitTestAssertionsShouldIncludeMessage"/> -->
<!-- <rule ref="category/java/bestpractices.xml/UnitTestContainsTooManyAsserts"/> -->
<!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldIncludeAssert"/> -->
<rule ref="category/java/bestpractices.xml/JUnitUseExpected"/>
<rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"/>
<rule ref="category/java/bestpractices.xml/LooseCoupling"/>
<rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/>
<rule ref="category/java/bestpractices.xml/MissingOverride"/>
<rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/>
<rule ref="category/java/bestpractices.xml/PreserveStackTrace"/>
<rule ref="category/java/bestpractices.xml/PrimitiveWrapperInstantiation"/>
<rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/>
<rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/>
<rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/>
<rule ref="category/java/bestpractices.xml/SimplifiableTestAssertion"/>
<rule ref="category/java/bestpractices.xml/NonExhaustiveSwitch"/>
<rule ref="category/java/bestpractices.xml/UnusedAssignment"/>
<rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>
<rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
<rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
<rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
<rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/>
<rule ref="category/java/bestpractices.xml/UseStandardCharsets"/>
<rule ref="category/java/bestpractices.xml/WhileLoopWithLiteralBoolean"/>
</ruleset>

160
pom.xml 100644
View File

@ -0,0 +1,160 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.hs_mannheim.pr2</groupId>
<artifactId>solutions</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>PR2 Excercises</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<junit.jupiter.version>5.12.2</junit.jupiter.version>
<junit.platform.version>1.12.2</junit.platform.version>
<pmdVersion>7.13.0</pmdVersion>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.4</version>
</dependency>
</dependencies>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.26.0</version>
<dependencies>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>
<version>${pmdVersion}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>${pmdVersion}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-javascript</artifactId>
<version>${pmdVersion}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-jsp</artifactId>
<version>${pmdVersion}</version>
</dependency>
</dependencies>
<configuration>
<rulesets>
<ruleset>./pmd.xml</ruleset>
</rulesets>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!--
<dependency>
<groupId>de.smits_net.games</groupId>
<artifactId>game-framework</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>com.github.thomsmits</groupId>
<artifactId> game-framework</artifactId>
<version>v1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>${junit.platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.3</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,196 @@
package de.hs_mannheim.pr2;
import java.util.Comparator;
import java.util.Random;
/**
* Hello world!
*/
public class Skiplist<T> {
Node head = null;
int size = 0;
int lvl = 1;
Comparator<? super T> comparator = null;
Random rng = new Random();
int cpr(T x, T y) {
return comparator != null ? comparator.compare(x, y) : ((Comparable<T>)x).compareTo(y);
}
public Skiplist() {
comparator = null;
head = new Node(null, true);
}
public Skiplist(Comparator<? super T> comparator) {
this.comparator = comparator;
head = new Node(null, true);
}
public int size() {
return size;
}
public boolean empty() {
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;
}
}
public void add(T data) {
Node node = new Node(data);
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;
}
counter++;
}
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;
}
}
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;
}
public T get(int location) {
if (location >= size) {
return null;
}
Node currentNode = head;
while (currentNode.down != null) {
currentNode = currentNode.down;
}
currentNode = currentNode.next;
for (int i = 0; i < location; i++) {
currentNode = currentNode.next;
}
return currentNode.value;
}
@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;
}
currentHead = currentHead.down;
result.append("\n");
}
return result.toString();
}
// @Override
// public String toString() {
// return head.toString();
// }
private class Node {
Node next = null;
Node down = null;
T value;
boolean dummy = false;
Node(T value) {
this.value = value;
}
Node(T value, Boolean dummy) {
this.dummy = true;
}
@Override
public String toString() {
// String result = "";
// if (this.down != null) {
// result += this.down.toString();
// result += " --v\n";
// }
// if (this.next != null) {
// result += this.next.toString();
// }
if (dummy) {
return "[dummy]";
} else {
return "[" + value + "]";
}
// return result;
}
}
}

View File

@ -0,0 +1,78 @@
package de.hs_mannheim.pr2;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
/**
* Unit test for simple App.
*/
public class SkiplistTest {
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue() {
assertTrue(true);
}
@Test
public void canPush() {
Skiplist<Integer> list = new Skiplist<Integer>();
list.add(1);
list.add(3);
list.add(2);
list.add(4);
list.add(10);
list.add(7);
list.add(8);
list.add(6);
list.add(3);
System.out.println(list);
assertEquals(9, list.size());
}
@Test
public void canPop() {
Skiplist<Integer> list = new Skiplist<Integer>();
list.add(1);
list.add(3);
list.add(2);
list.add(4);
list.add(10);
list.add(7);
list.add(8);
list.add(6);
list.add(3);
System.out.println(list);
assertEquals(1, list.pop().intValue());
System.out.println(list);
assertEquals(2, list.pop().intValue());
System.out.println(list);
assertEquals(3, list.pop().intValue());
System.out.println(list);
assertEquals(3, list.pop().intValue());
System.out.println(list);
assertEquals(4, list.pop().intValue());
System.out.println(list);
assertEquals(6, list.pop().intValue());
System.out.println(list);
assertTrue(list.size() == 3);
}
@Test
public void canGet() {
Skiplist<Integer> list = new Skiplist<Integer>();
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));
}
}