Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions linkedAfter/.cs50.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
submit50:
files: &submit50_files
- !exclude "*"
- !include "*.py"
- !require linkedAfter.py

check50:
files: *submit50_files
checks: checks/main.py

Empty file added linkedAfter/checks/__init__.py
Empty file.
160 changes: 160 additions & 0 deletions linkedAfter/checks/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import check50
import check50.py

FILE_NAME = "linkedAfter.py"


def to_list(ll):
"""LinkedList -> Python-Liste (mit Zyklus-Schutz)."""
out = []
cur = ll.head
seen = 0
while cur is not None:
out.append(cur.value)
cur = cur.next
seen += 1
if seen > 10_000:
msg = f"Linked list appears to have a cycle (more than {seen} nodes)"
raise check50.Failure(msg)
return out


def build_ll(module, values):
"""Erzeugt eine LinkedList über FromPythonlist (wie in der Aufgabe vorgegeben)."""
ll = module.LinkedList()
ll.FromPythonlist(values)
return ll


def a(self, b):
c = self.head
while c is not None:
if c.value == b:
return c
c = c.next
return None
Comment on lines +29 to +35
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name a with parameter b and variable c is not descriptive. This appears to be a helper function for searching values in a linked list. Consider using meaningful names like search_value_impl for the function, value for the parameter, and current for the variable to improve code readability.

Copilot uses AI. Check for mistakes.


@check50.check()
def exists():
"""linkedAfter.py exists"""
check50.exists(FILE_NAME)


@check50.check(exists)
def compiles():
"""linkedAfter.py compiles"""
check50.py.compile(FILE_NAME)


@check50.check(compiles)
def has_classes_and_method():
"""ListNode, LinkedList and insertAfterNode exist"""
module = check50.py.import_(FILE_NAME)

if not hasattr(module, "ListNode"):
msg = f"Class `ListNode` not found in {FILE_NAME}"
raise check50.Failure(msg)

if not hasattr(module, "LinkedList"):
msg = f"Class `LinkedList` not found in {FILE_NAME}"
raise check50.Failure(msg)

if not hasattr(module.LinkedList(), "insertAfterNode"):
msg = "Method `insertAfterNode` not found in Class `LinkedList`"
raise check50.Failure(msg)


@check50.check(has_classes_and_method)
def test_frompythonlist_order_is_head_insertion():
"""FromPythonlist: inserts after head (result is reversed input order)"""
module = check50.py.import_(FILE_NAME)

ll = build_ll(module, [1, 2, 8, 4])
expected = [4, 8, 2, 1] # weil insertAfterHead bei Vorwärtsiteration umdreht
actual = to_list(ll)

if actual != expected:
raise check50.Mismatch(str(expected), str(actual))


@check50.check(has_classes_and_method)
def test_insert_after_middle():
"""insertAfterNode inserts after a middle node (in reversed-built list)"""
module = check50.py.import_(FILE_NAME)

ll = build_ll(module, [1, 2, 8, 4]) # Liste: 4 -> 8 -> 2 -> 1
module.LinkedList.searchValue = a
node = ll.searchValue(8)
if node is None:
msg = "searchValue(8) returned None, but 8 should be in the list"
raise check50.Failure(msg)

ll.insertAfterNode(node, module.ListNode(3))

expected = [4, 8, 3, 2, 1]
actual = to_list(ll)
if actual != expected:
raise check50.Mismatch(str(expected), str(actual))


@check50.check(has_classes_and_method)
def test_insert_after_head():
"""insertAfterNode inserts after head"""
module = check50.py.import_(FILE_NAME)

ll = build_ll(module, [10, 20, 30]) # Liste: 30 -> 20 -> 10
module.LinkedList.searchValue = a
head = ll.head
if head is None:
msg = "Linked list head is None after FromPythonlist"
raise check50.Failure(msg)

ll.insertAfterNode(head, module.ListNode(25))

expected = [30, 25, 20, 10]
actual = to_list(ll)
if actual != expected:
raise check50.Mismatch(str(expected), str(actual))


@check50.check(has_classes_and_method)
def test_insert_after_tail():
"""insertAfterNode inserts after last node (tail)"""
module = check50.py.import_(FILE_NAME)

ll = build_ll(module, [10, 20, 30]) # Liste: 30 -> 20 -> 10
module.LinkedList.searchValue = a
tail = ll.searchValue(10) # 10 ist Tail
if tail is None:
msg = "searchValue(10) returned None, but 10 should be in the list"
raise check50.Failure(msg)

ll.insertAfterNode(tail, module.ListNode(5))

expected = [30, 20, 10, 5]
actual = to_list(ll)
if actual != expected:
raise check50.Mismatch(str(expected), str(actual))


@check50.check(has_classes_and_method)
def test_insert_after_none_prevnode():
"""insertAfterNode with prevNode=None:
We expect an exception (ValueError) rather than silently doing nothing."""
module = check50.py.import_(FILE_NAME)

ll = build_ll(module, [1, 2, 3])
module.LinkedList.searchValue = a
prev = ll.searchValue(999) # not in list -> None
new_node = module.ListNode(4)

threw = False
try:
ll.insertAfterNode(prev, new_node)
except ValueError:
threw = True

if not threw:
msg = "insertAfterNode should raise a ValueError when prevNode is None"
raise check50.Failure(msg)
204 changes: 204 additions & 0 deletions linkedAfter/html/linkedAfter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<h1 id="linkedAfter">Linked List: Insert After Node</h1>

<p>
In dieser Übung werden Sie die Datenstruktur <strong>Verkettete Liste (Linked List)</strong> erweitern.
Gegeben sind bereits die Klassen <code>ListNode</code> und <code>LinkedList</code>, sowie einige
Hilfsmethoden: <code>FromPythonlist(inputList)</code> zur Erzeugung einer verketteten Liste aus einer Python-Liste
und <code>printList()</code> zur Ausgabe der Liste. Ebenfalls gegeben ist die Methode
<code>searchValue(value)</code>, mit deren Hilfe Sie ein Listenelement mit einem bestimmten Wert
suchen können. Ihre Aufgabe ist es, die Methode <code>insertAfterNode(prevNode, newNode)</code>
zu implementieren, die ein neues Listenelement <code>newNode</code> direkt nach dem gegebenen
Element <code>prevNode</code> einfügt.
</p>


<h2 id="aufgabe">Aufgabe</h2>

<p>
Implementieren Sie die Methode <code>insertAfterNode(self, prevNode, newNode)</code> in der Klasse <code>LinkedList</code>.
Die Methode erhält das Element <code>prevNode</code> und das neue Element <code>newNode</code>
und soll das neue Element direkt nach <code>prevNode</code> in die Liste einfügen. Ist <code>prevNode</code> <code>None</code>,
soll eine <code>ValueError</code>-Exception ausgelöst werden.
</p>


<p>
Erstellen Sie eine Datei mit dem Namen
<code class="text-danger border border-dark">linkedAfter.py</code> und
Implementieren Sie die Methode <code>insertAfterNode</code> in Ihrer Datei. Kopieren Sie dazu den
folgenden Codeblock in die Datei <code class="text-danger border border-dark">linkedAfter.py</code>,
und ergänzen Sie die Methode <code>insertAfterNode</code>:
</p>

<div class="alert alert-dark">
<pre><code>
class ListNode:
def __init__(self, value: int):
self.value: int = value
self.next: ListNode | None = None


class LinkedList:
def __init__(self):
self.head = None

def FromPythonlist(self, inputList: list[int]):
for val in inputList:
newNode = ListNode(value=val)
self.insertAfterHead(newNode)

def searchValue(self, value: int) -> ListNode:
# Siehe Problem 'linkedFind.py' für die Implementierung dieser Methode
return

def insertAfterNode(self, prevNode: ListNode | None, newNode: ListNode):
# TODO: Implementieren Sie diese Methode
return

def printList(self):
current = self.head
while current is not None:
print(current.value, end=" -> ")
current = current.next
print("None")

def insertAfterHead(self, newNode: ListNode):
newNode.next = self.head
self.head = newNode
</code></pre>
</div>


<!-- <p></p>
<h2 id="demo">Demo</h2>
<p>
<script async="" data-autoplay="1" data-cols="80" data-loop="1" data-rows="12"
id="asciicast-"
src="https://asciinema.org/a/.js">
</script>
</p> -->

<h2 id="before-you-begin">Bevor Sie beginnen</h2>
<p>Melden Sie sich bei <a href="https://cs50.dev/" target="_blank">cs50.dev</a> an, klicken Sie
auf Ihr Terminal und führen Sie <code
class="text-danger border border-dark">cd</code> ohne Parameter aus. Sie
sollten feststellen, dass der Prompt Ihres Terminals wie unten aussieht:</p>
<div class="alert alert-primary"><code
class="text-dark font-weight-bold">$</code></div>
<p>Als nächstes führen Sie</p>
<div class="alert alert-primary"><code class="text-dark font-weight-bold">mkdir
linkedAfter</code></div>
<p>aus, um einen Ordner namens <code
class="text-danger border border-dark">linkedAfter</code> in Ihrem Codespace
zu erstellen.</p>
<p>Führen Sie anschließend</p>
<div class="alert alert-primary"><code class="text-dark font-weight-bold">cd
linkedAfter</code></div>
<p>aus, um in dieses Verzeichnis zu wechseln. Der Prompt Ihres Terminals sollte
nun <code class="text-danger border border-dark">linkedAfter/$</code> anzeigen.
Jetzt können Sie</p>
<div class="alert alert-primary"><code class="text-dark font-weight-bold">code
linkedAfter.py</code></div>
<p>ausführen, um eine Datei namens <code
class="text-danger">linkedAfter.py</code> zu erstellen, in der Sie Ihr
Programm schreiben.
</p>

<h2 id="how-to-test">So testen Sie Ihr Programm</h2>
<p>So können Sie Ihr Programm manuell testen:</p>

<p>
Testen Sie Ihre Implementierung, indem Sie das Skript ausführen.
Erstellen Sie dazu eine Instanz der <code>LinkedList</code>, fügen Sie Elemente hinzu und rufen Sie dann Ihre Methode <code>insertAfterNode</code> auf.
Überprüfen Sie anschließend, ob die Liste die erwartete Struktur hat, indem Sie <code>printList()</code> verwenden.
</p>

<div class="alert alert-dark">
<pre><code>
# Erstellen einer verketteten Liste aus einer Python-Liste
inputList = [4, 8, 2, 1]
ll = LinkedList()
ll.FromPythonlist(inputList)

print("Liste vorher:")
ll.printList()

node = ll.searchValue(8)
newNode = ListNode(3)
# Einfügen von 3 nach 8
ll.insertAfterNode(node, newNode)

print("Liste nachher:")
ll.printList()
# Erwartete Ausgabe: 1 -> 2 -> 8 -> 3 -> 4 -> None
</code></pre>
</div>



<p>Sie können das folgende Kommando ausführen, um Ihren Code mit <code
class="text-danger">check50</code> zu überprüfen, einem Programm, das bei
der Abgabe verwendet wird um Ihren Code zu testen. Testen Sie Ihr Programm
aber auch selbst!</p>
<div class="alert alert-primary"><code class="text-dark font-weight-bold">
check50 HSDDigitalLabor/problems/adg2025/linkedAfter
</code></div>
<p>
Grüne Smileys <span style="font-family: monospace; color: green;">:) </span>
bedeuten, dass Ihr Programm einen Test bestanden hat! Rote
traurige Smileys <span style="font-family: monospace; color: red;">:( </span>
zeigen an, dass Ihr Programm etwas Unerwartetes ausgegeben
hat. Besuchen Sie die URL, die <code class="text-danger">check50</code>
ausgibt, um zu sehen, welche Eingabe <code class="text-danger">check50</code>
an Ihr Programm übergeben hat, welche Ausgabe erwartet wurde und welche
Ausgabe Ihr Programm tatsächlich geliefert hat.
</p>

<h2 id="how-to-submit">Abgabe</h2>
<p>Führen Sie im Terminal den folgenden Befehl aus, um Ihre Arbeit einzureichen.
</p>
<div class="alert alert-primary"><code class="text-dark font-weight-bold">
submit50 HSDDigitalLabor/problems/adg2025/linkedAfter
</code></div>
<p>
Führen Sie diesen Befehl vor dem Fälligkeitsdatum aus. Sie können Ihre Lösung
nach dem ersten Einsenden noch ändern und erneut einreichen. Bewertet wird
die zuletzt eingereichte Version vor dem Fälligkeitsdatum. Nach Ablauf der
Frist können Sie technisch zwar noch Abgaben tätigen, Ihre Lösung wird jedoch
nicht mehr gewertet. Das Ergebnis kann ausschließlich mit obigem Befehl abgegeben
werden, eine Abgabe in Moodle ist nicht möglich.
</p>

<h2 id="how-to-mark">Markierung der Aufgabe als erledigt</h2>
<p>Nach dem Einreichen der Lösung mit <code class="text-danger">submit50</code>,
nehmen Sie eine Pseudolösung in Moodle vor, indem Sie auf den Button
<button type="submit" class="btn btn-primary" id="sample_solution" disabled>
Aufgabenlösung hinzufügen</button>
weiter unten auf dieser Seite betätigen und in das sich öffnende Feld unter
"Texteingabe online" den folgenden Text ein:
</p>
<div class="alert alert-primary">
<code class="text-dark font-weight-bold">Mit submit50 abgegeben.</code>
</div>
<p>Schließen Sie die Eingabe durch Klick auf den Button
<button type="submit" class="btn btn-primary" id="sample_save" disabled>
Änderungen speichern</button> unterhalb des Textfeldes ab.
</p>
<p>
Zur besseren Übersicht markieren Sie das Problem linkedAfter.py in Moodle als erledigt,
indem Sie ganz oben auf dieser Seite den Button
</p>
<p>
<button class="btn btn-outline-secondary btn-sm text-nowrap" title="Problem: linkedAfter.py als erledigt kennzeichnen"
aria-label="Problem: linkedAfter.py als erledigt kennzeichnen" disabled>
Als erledigt kennzeichnen
</button>
klicken. Daraufhin erscheint der Button
<button class="btn btn-success btn-sm text-nowrap"
title="Problem: linkedAfter.py ist als erledigt gekennzeichnet. Zum Rückgängigmachen klicken."
aria-label="Problem: linkedAfter.py ist als erledigt gekennzeichnet. Zum Rückgängigmachen klicken." disabled>
<i class="fa fa-check" aria-hidden="true"></i> Erledigt
</button>
</p>

<p class="text-muted text-right text-nowrap" style="font-size: 0.6em;">generated 2025-12-22 12:27:59</p>
9 changes: 9 additions & 0 deletions linkedAfter/html/params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "linkedAfter",
"title": "Linked List: Insert After Node",
"foldername": "linkedAfter",
"filename": "linkedAfter.py",
"asciicast_id": "",
"check50_path": "HSDDigitalLabor/problems/adg2025/linkedAfter",
"submit50_path": "HSDDigitalLabor/problems/adg2025/linkedAfter"
}
Loading