-
Notifications
You must be signed in to change notification settings - Fork 0
add Problem "linkedAfter" #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
6fcb23f
aea32c1
fb98a5c
3ff0095
3277d1b
236fa33
bb01459
bade0eb
e1706f4
7e1c5a6
d652a21
83c18b4
0ba9f90
7e2612e
e4adf45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
|
|
| 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 | ||||||
|
|
||||||
|
|
||||||
| @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 an ValueError when prevNode is None" | ||||||
|
||||||
| msg = "insertAfterNode should raise an ValueError when prevNode is None" | |
| msg = "insertAfterNode should raise a ValueError when prevNode is None" |
| 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: | ||||||||||
|
||||||||||
| def searchValue(self, value: int) -> ListNode: | |
| def searchValue(self, value: int) -> ListNode | None: |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expected output comment is incorrect. Given the list construction method using insertAfterHead (which reverses the order), the list starts as [4, 8, 2, 1]. After inserting 3 after 8, it should be [4, 8, 3, 2, 1], not [1, 2, 8, 3, 4]. This comment could mislead students about the expected behavior.
| # Erwartete Ausgabe: 1 -> 2 -> 8 -> 3 -> 4 -> None | |
| # Erwartete Ausgabe: 4 -> 8 -> 3 -> 2 -> 1 -> None |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expected output in the documentation is incorrect. The comment states "Erwartete Ausgabe: 1 -> 2 -> 8 -> 3 -> 4 -> None", but based on the FromPythonlist implementation which uses insertAfterHead, the list is actually built in reverse order. For inputList = [1, 2, 8, 4], the resulting list will be 4 -> 8 -> 2 -> 1, so after inserting 3 after 8, the expected output should be "4 -> 8 -> 3 -> 2 -> 1 -> None", not "1 -> 2 -> 8 -> 3 -> 4 -> None".
| # Erwartete Ausgabe: 1 -> 2 -> 8 -> 3 -> 4 -> None | |
| # Erwartete Ausgabe: 4 -> 8 -> 3 -> 2 -> 1 -> None |
| 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" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation inconsistency: The multi-line string states "We expect an exception (ValueError)" but doesn't specify which exception type using proper formatting. Consider using backticks around ValueError for consistency with code documentation standards.