uebung_03: implement Task 1 (IntLinkedList) and Task 2 (generic LinkedList)
Doubly linked list with head/tail references for int values (Task 1) and a generic LinkedList<T>/ListNode<T> version (Task 2). Both implement add, get, remove, getSize with O(n/2) walks via the closer-end heuristic. Includes IntLinkedListTest covering empty, single-element, head/tail/middle removal, and out-of-bounds cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
110
uebung_03/src/IntLinkedList.java
Normal file
110
uebung_03/src/IntLinkedList.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
public class IntLinkedList {
|
||||||
|
private IntListNode headNode;
|
||||||
|
private IntListNode tailNode;
|
||||||
|
private int size;
|
||||||
|
public IntLinkedList() {
|
||||||
|
headNode = null;
|
||||||
|
tailNode = null;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
public void add(int value) {
|
||||||
|
if (headNode == null) {
|
||||||
|
headNode = new IntListNode(value, null, null);
|
||||||
|
tailNode = headNode;
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
IntListNode newNode = new IntListNode(value, null, null);
|
||||||
|
newNode.prevNode = tailNode;
|
||||||
|
tailNode.nextNode = newNode;
|
||||||
|
tailNode = newNode;
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
public int get(int index){
|
||||||
|
if (index < 0 || index > (size - 1)){
|
||||||
|
throw new IllegalArgumentException("Index Out of Bounds");
|
||||||
|
}
|
||||||
|
if (index < size/2){
|
||||||
|
int pos = 0;
|
||||||
|
IntListNode curNode = headNode;
|
||||||
|
while (pos < index){
|
||||||
|
curNode = curNode.nextNode;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return curNode.value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int pos = size - 1;
|
||||||
|
IntListNode curNode = tailNode;
|
||||||
|
while (pos > index){
|
||||||
|
curNode = curNode.prevNode;
|
||||||
|
pos --;
|
||||||
|
}
|
||||||
|
return curNode.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(int index){
|
||||||
|
if (index < 0 || index > (size - 1)){
|
||||||
|
throw new IllegalArgumentException("Index Out of Bounds");
|
||||||
|
}
|
||||||
|
if(index == 0){
|
||||||
|
if(size == 1){
|
||||||
|
headNode = null;
|
||||||
|
tailNode = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
headNode = headNode.nextNode;
|
||||||
|
headNode.prevNode = null;
|
||||||
|
}
|
||||||
|
size --;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index == (size - 1)){
|
||||||
|
|
||||||
|
tailNode = tailNode.prevNode;
|
||||||
|
tailNode.nextNode = null;
|
||||||
|
size --;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < size/2){
|
||||||
|
int pos = 0;
|
||||||
|
IntListNode curNode = headNode;
|
||||||
|
while (pos < index){
|
||||||
|
curNode = curNode.nextNode;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
// Set the prev of the following node so pos +1
|
||||||
|
curNode.nextNode.prevNode = curNode.prevNode;
|
||||||
|
|
||||||
|
// Then set the next of the previous node;
|
||||||
|
curNode.prevNode.nextNode = curNode.nextNode;
|
||||||
|
|
||||||
|
size--;
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int pos = size - 1;
|
||||||
|
IntListNode curNode = tailNode;
|
||||||
|
while (pos > index){
|
||||||
|
curNode = curNode.prevNode;
|
||||||
|
pos --;
|
||||||
|
}
|
||||||
|
// Set the prev of the following node so pos +1
|
||||||
|
curNode.nextNode.prevNode = curNode.prevNode;
|
||||||
|
|
||||||
|
// Then set the next of the previous node;
|
||||||
|
curNode.prevNode.nextNode = curNode.nextNode;
|
||||||
|
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
166
uebung_03/src/IntLinkedListTest.java
Normal file
166
uebung_03/src/IntLinkedListTest.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
public class IntLinkedListTest {
|
||||||
|
|
||||||
|
static int passed = 0;
|
||||||
|
static int failed = 0;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
testEmpty();
|
||||||
|
testAddOne();
|
||||||
|
testAddMany();
|
||||||
|
testGetAllPositions();
|
||||||
|
testGetFromBothSides();
|
||||||
|
testGetOutOfBounds();
|
||||||
|
|
||||||
|
testRemoveOnlyElement();
|
||||||
|
testRemoveHead();
|
||||||
|
testRemoveTail();
|
||||||
|
testRemoveMiddleHeadWalk();
|
||||||
|
testRemoveMiddleTailWalk();
|
||||||
|
testRemoveOutOfBounds();
|
||||||
|
testRemoveThenAdd();
|
||||||
|
testRemoveAllSequentially();
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Passed: " + passed + " / " + (passed + failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEmpty() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
check("empty size", l.getSize(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAddOne() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(42);
|
||||||
|
check("size after 1 add", l.getSize(), 1);
|
||||||
|
check("get(0) after 1 add", l.get(0), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAddMany() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
for (int i = 0; i < 5; i++) l.add(i * 10);
|
||||||
|
check("size after 5 adds", l.getSize(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testGetAllPositions() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
int[] vals = {10, 20, 30, 40, 50, 60};
|
||||||
|
for (int v : vals) l.add(v);
|
||||||
|
for (int i = 0; i < vals.length; i++) {
|
||||||
|
check("get(" + i + ")", l.get(i), vals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testGetFromBothSides() {
|
||||||
|
// size=4 -> indices 0,1 walk from head; 2,3 walk from tail
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3); l.add(4);
|
||||||
|
check("get(0) head-walk", l.get(0), 1);
|
||||||
|
check("get(1) head-walk", l.get(1), 2);
|
||||||
|
check("get(2) tail-walk", l.get(2), 3);
|
||||||
|
check("get(3) tail-walk", l.get(3), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testGetOutOfBounds() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2);
|
||||||
|
try { l.get(-1); System.out.println("FAIL get(-1) should throw"); failed++; }
|
||||||
|
catch (IllegalArgumentException e) { System.out.println("PASS get(-1) throws"); passed++; }
|
||||||
|
try { l.get(2); System.out.println("FAIL get(size) should throw"); failed++; }
|
||||||
|
catch (IllegalArgumentException e) { System.out.println("PASS get(size) throws"); passed++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveOnlyElement() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(99);
|
||||||
|
l.remove(0);
|
||||||
|
check("remove only: size", l.getSize(), 0);
|
||||||
|
// Re-add to make sure head/tail were properly reset
|
||||||
|
l.add(5);
|
||||||
|
check("remove only then add: size", l.getSize(), 1);
|
||||||
|
check("remove only then add: get(0)", l.get(0), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveHead() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3);
|
||||||
|
l.remove(0);
|
||||||
|
check("remove head: size", l.getSize(), 2);
|
||||||
|
check("remove head: get(0)", l.get(0), 2);
|
||||||
|
check("remove head: get(1)", l.get(1), 3); // uses tail-walk -> tests prev chain
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveTail() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3);
|
||||||
|
l.remove(2);
|
||||||
|
check("remove tail: size", l.getSize(), 2);
|
||||||
|
check("remove tail: get(0)", l.get(0), 1);
|
||||||
|
check("remove tail: get(1)", l.get(1), 2); // tail-walk -> new tail must be correct
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveMiddleHeadWalk() {
|
||||||
|
// size=4, remove index 1 (< size/2 -> head-walk branch)
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3); l.add(4);
|
||||||
|
l.remove(1);
|
||||||
|
check("remove mid (head-walk): size", l.getSize(), 3);
|
||||||
|
check("remove mid (head-walk): get(0)", l.get(0), 1);
|
||||||
|
check("remove mid (head-walk): get(1)", l.get(1), 3);
|
||||||
|
check("remove mid (head-walk): get(2)", l.get(2), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveMiddleTailWalk() {
|
||||||
|
// size=6, remove index 4 (>= size/2 -> tail-walk branch)
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3); l.add(4); l.add(5); l.add(6);
|
||||||
|
l.remove(4);
|
||||||
|
check("remove mid (tail-walk): size", l.getSize(), 5);
|
||||||
|
check("remove mid (tail-walk): get(0)", l.get(0), 1);
|
||||||
|
check("remove mid (tail-walk): get(3)", l.get(3), 4);
|
||||||
|
check("remove mid (tail-walk): get(4)", l.get(4), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveOutOfBounds() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2);
|
||||||
|
try { l.remove(-1); System.out.println("FAIL remove(-1) should throw"); failed++; }
|
||||||
|
catch (IllegalArgumentException e) { System.out.println("PASS remove(-1) throws"); passed++; }
|
||||||
|
try { l.remove(2); System.out.println("FAIL remove(size) should throw"); failed++; }
|
||||||
|
catch (IllegalArgumentException e) { System.out.println("PASS remove(size) throws"); passed++; }
|
||||||
|
check("oob did not mutate: size", l.getSize(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveThenAdd() {
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
l.add(1); l.add(2); l.add(3);
|
||||||
|
l.remove(2); // remove tail
|
||||||
|
l.add(99); // new tail
|
||||||
|
check("remove-then-add: size", l.getSize(), 3);
|
||||||
|
check("remove-then-add: get(0)", l.get(0), 1);
|
||||||
|
check("remove-then-add: get(1)", l.get(1), 2);
|
||||||
|
check("remove-then-add: get(2)", l.get(2), 99);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRemoveAllSequentially() {
|
||||||
|
// Remove from head repeatedly until empty, then re-fill.
|
||||||
|
IntLinkedList l = new IntLinkedList();
|
||||||
|
for (int i = 1; i <= 5; i++) l.add(i);
|
||||||
|
while (l.getSize() > 0) l.remove(0);
|
||||||
|
check("remove all: size", l.getSize(), 0);
|
||||||
|
l.add(7); l.add(8);
|
||||||
|
check("refill after remove-all: size", l.getSize(), 2);
|
||||||
|
check("refill after remove-all: get(0)", l.get(0), 7);
|
||||||
|
check("refill after remove-all: get(1)", l.get(1), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check(String label, int actual, int expected) {
|
||||||
|
if (actual == expected) {
|
||||||
|
System.out.println("PASS " + label + " = " + actual);
|
||||||
|
passed++;
|
||||||
|
} else {
|
||||||
|
System.out.println("FAIL " + label + " expected=" + expected + " actual=" + actual);
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
uebung_03/src/IntListNode.java
Normal file
11
uebung_03/src/IntListNode.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
public class IntListNode {
|
||||||
|
int value;
|
||||||
|
IntListNode nextNode;
|
||||||
|
IntListNode prevNode;
|
||||||
|
|
||||||
|
public IntListNode(int value, IntListNode nextNode, IntListNode prevNode) {
|
||||||
|
this.value = value;
|
||||||
|
this.nextNode = nextNode;
|
||||||
|
this.prevNode = prevNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
uebung_03/src/LinkedList.java
Normal file
100
uebung_03/src/LinkedList.java
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
public class LinkedList<T> {
|
||||||
|
private ListNode<T> headNode;
|
||||||
|
private ListNode<T> tailNode;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public LinkedList() {
|
||||||
|
headNode = null;
|
||||||
|
tailNode = null;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(T value) {
|
||||||
|
if (headNode == null) {
|
||||||
|
headNode = new ListNode<>(value, null, null);
|
||||||
|
tailNode = headNode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ListNode<T> newNode = new ListNode<>(value, null, null);
|
||||||
|
newNode.prevNode = tailNode;
|
||||||
|
tailNode.nextNode = newNode;
|
||||||
|
tailNode = newNode;
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(int index) {
|
||||||
|
if (index < 0 || index > (size - 1)) {
|
||||||
|
throw new IllegalArgumentException("Index Out of Bounds");
|
||||||
|
}
|
||||||
|
if (index < size / 2) {
|
||||||
|
int pos = 0;
|
||||||
|
ListNode<T> curNode = headNode;
|
||||||
|
while (pos < index) {
|
||||||
|
curNode = curNode.nextNode;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return curNode.value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int pos = size - 1;
|
||||||
|
ListNode<T> curNode = tailNode;
|
||||||
|
while (pos > index) {
|
||||||
|
curNode = curNode.prevNode;
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
return curNode.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(int index) {
|
||||||
|
if (index < 0 || index > (size - 1)) {
|
||||||
|
throw new IllegalArgumentException("Index Out of Bounds");
|
||||||
|
}
|
||||||
|
if (index == 0) {
|
||||||
|
if (size == 1) {
|
||||||
|
headNode = null;
|
||||||
|
tailNode = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
headNode = headNode.nextNode;
|
||||||
|
headNode.prevNode = null;
|
||||||
|
}
|
||||||
|
size--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index == (size - 1)) {
|
||||||
|
tailNode = tailNode.prevNode;
|
||||||
|
tailNode.nextNode = null;
|
||||||
|
size--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < size / 2) {
|
||||||
|
int pos = 0;
|
||||||
|
ListNode<T> curNode = headNode;
|
||||||
|
while (pos < index) {
|
||||||
|
curNode = curNode.nextNode;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
curNode.nextNode.prevNode = curNode.prevNode;
|
||||||
|
curNode.prevNode.nextNode = curNode.nextNode;
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int pos = size - 1;
|
||||||
|
ListNode<T> curNode = tailNode;
|
||||||
|
while (pos > index) {
|
||||||
|
curNode = curNode.prevNode;
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
curNode.nextNode.prevNode = curNode.prevNode;
|
||||||
|
curNode.prevNode.nextNode = curNode.nextNode;
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
uebung_03/src/ListNode.java
Normal file
11
uebung_03/src/ListNode.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
public class ListNode<T> {
|
||||||
|
T value;
|
||||||
|
ListNode<T> nextNode;
|
||||||
|
ListNode<T> prevNode;
|
||||||
|
|
||||||
|
public ListNode(T value, ListNode<T> nextNode, ListNode<T> prevNode) {
|
||||||
|
this.value = value;
|
||||||
|
this.nextNode = nextNode;
|
||||||
|
this.prevNode = prevNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user