from typing import Any
class NodeLL():
def __init__(self,data: Any|None=None):
self.data: Any|None = data
self.next_node: NodeLL|None = None
class NodeDLL():
def __init__(self,data: Any|None=None):
self.data: Any|None = data
self.prev_node: NodeDLL|None = None
self.prev_node: NodeDLL|None = None
class LinkedList():
def __init__(self, first_node: NodeLL|None=None):
self.first_node: NodeLL|None = first_node
class DoublyLinkedList():
def __init__(self, first_node: NodeDLL|None=None,last_node: NodeDLL|None=None):
self.first_node: NodeDLL|None = first_node
self.last_node: NodeDLL|None = last_node
0. Setup Node
, LinkedList
& DoublyLinkedList
Class
# classic nodes setup
=NodeLL("time")
node0_ll=NodeLL("of")
node1_ll=NodeLL("your")
node2_ll=NodeLL("life")
node3_ll=node1_ll
node0_ll.next_node=node2_ll
node1_ll.next_node=node3_ll node2_ll.next_node
# doubly nodes setup
=NodeDLL("TIME")
node0_dll=NodeDLL("OF")
node1_dll=NodeDLL("YOUR")
node2_dll=NodeDLL("LIFE")
node3_dll
=node1_dll
node0_dll.next_node=node2_dll
node1_dll.next_node=node3_dll
node2_dll.next_node
=node0_dll
node1_dll.prev_node=node1_dll
node2_dll.prev_node=node2_dll node3_dll.prev_node
1. Add method: print_all
to Classic LinkedList
Class
def print_all_ll(linked_list: LinkedList)-> None:
= linked_list.first_node
current_node
# case 1: empty list: print empty list
if not current_node:
print("Empty LinkedList!")
return None
# case 2: len(ll)==1: print ll[0] # captured in case 3
# if not current_node.next_node:
# print(current_node.data)
# return None
# case 3: n items
while current_node:
print(current_node.data) # print node.data
= current_node.next_node # go next node
current_node return None
# empty list
print_all_ll(LinkedList()) print()
"single lonely node"))) # empty list
print_all_ll(LinkedList(NodeLL(print()
print_all_ll(LinkedList(node0_ll))
print()
Empty LinkedList!
single lonely node
time
of
your
life
2a. Add method: print_all_reverse
to ClassicLinkedList
Class
def print_rev_all_ll(linked_list: LinkedList)-> None:
= linked_list.first_node
current_node = []
node_list # case 1: empty list: print empty list
if not current_node:
print("Empty LinkedList!")
return None
# case n: n items
while current_node:
# print(current_node.data) # print node.data
node_list.append(current_node.data)= current_node.next_node # go next node
current_node
print(node_list[i]) for i in range(len(node_list)-1,-1,-1)]
[
return None
# LL - REVERSE
# empty list
print_rev_all_ll(LinkedList()) print()
"single lonely node"))) # empty list
print_rev_all_ll(LinkedList(NodeLL(print()
print_rev_all_ll(LinkedList(node0_ll))print()
# eta: 9 mins
# Comments: used [1.soln], create [node_list] and append each [node.data] then [reverse print]
Empty LinkedList!
single lonely node
life
your
of
time
2b. Add method: print_all_reverse
to DoublyLinkedList
Class
def print_rev_all_dll(dbly_linked_list: DoublyLinkedList)-> None:
= dbly_linked_list.last_node
current_node
# case 1: empty list: print empty list
if not current_node:
print("Empty LinkedList!")
return None
# case 3: n items
while current_node:
print(current_node.data) # "last"
= current_node.prev_node # go prev node
current_node return None
# DLL REVERSE
# empty list
print_rev_all_dll(DoublyLinkedList()) print()
# SINGLE NODE - DLL
print_rev_all_dll(DoublyLinkedList( =NodeDLL("single lonely node"), # node is first_node
first_node=NodeDLL("single lonely node"))) # and last_node
last_node
print()
print_rev_all_dll(DoublyLinkedList(=node0_dll, last_node=node3_dll))
first_nodeprint()
# eta: 15 min
# comments:
# - found & fixed bug prev_node assignments,
# - [2] update assn current_node -> prev_node and
# - [12] while loop to update to current_node to prev_node
Empty LinkedList!
single lonely node
LIFE
YOUR
OF
TIME
3a. Add method: return_last_item
to Classic LinkedList
Class
- Number of Elements: Unknown
def return_last_item_ll(linked_list: LinkedList)-> None:
= linked_list.first_node
current_node # node_list = []
# case 1: empty list: print empty list
if not current_node:
print("Empty LinkedList!")
return None
# case n: n items
while current_node.next_node:
# print(current_node.data) # print node.data
# node_list.append(current_node.data)
= current_node.next_node # go next node
current_node
# [print(node_list[i]) for i in range(len(node_list)-1,-1,-1)]
print(current_node.data)
# return current_node.data
# LL - last item
# empty list
return_last_item_ll(LinkedList()) print()
"single lonely node"))) # empty list
return_last_item_ll(LinkedList(NodeLL(print()
return_last_item_ll(LinkedList(node0_ll))print()
# eta: 3 mins
# Comments: used [1.soln] [11] while current_next.next_node exist then not last and go next
# [11/14] if c_node.next_node is falsy, its last node, leave while-loop.
Empty LinkedList!
single lonely node
life
3b. Add recursive method: return_last_item
to Classic LinkedList
Class
# recursive attempt - 1
def return_last_item_ll_recursivefn(linked_list: LinkedList)-> None:
= linked_list.first_node
current_node if not current_node:
print("Empty LinkedList!")
return None
if current_node.next_node:
= current_node.next_node
current_node = LinkedList(current_node) # start from next node
current_linked_list return return_last_item_ll_recursivefn(current_linked_list)
else:
print(current_node.data)
return current_node.data
# 'time','of','your','life'
# empty list
return_last_item_ll_recursivefn(LinkedList()) print()
"single lonely node")))
return_last_item_ll_recursivefn(LinkedList(NodeLL(print()
return_last_item_ll_recursivefn(LinkedList(node0_ll))print()
# eta: 20 mins
# comments: had to remind myself how recursive functions work
# [10] then thought of creating a new list each time current_node updates
# bad but worked
Empty LinkedList!
single lonely node
life
3c. Add recursive method: return_last_item
with Nodes
# recursive attempt - 2
def return_last_item_nodes_recursivefn(current_node: NodeLL)-> None:
# current_node = linked_list.first_node
if not current_node.data:
print("Empty Node!")
return None
if current_node.next_node:
= current_node.next_node
current_node # current_linked_list = LinkedList(current_node) # start from next node
return return_last_item_nodes_recursivefn(current_node)
else:
print(current_node.data)
return current_node.data
# 'time','of','your','life'
# empty list
return_last_item_nodes_recursivefn(NodeLL()) print()
"single lonely node")))
return_last_item_nodes_recursivefn((NodeLL(print()
return_last_item_nodes_recursivefn(node0_ll)print()
# eta: 2mins
# comments: adapt first attempt and remove the creation of lists.
Empty Node!
single lonely node
life
[ignore] 3d. Re-wrote to help me attempt 3b and 3c
def max_val(arr):
if len(arr)==1:
return arr[0]
if arr[0]>max_val(arr[1:]):
return arr[0]
else:
return max_val(arr[1:])
=[1,7,3,5,9,4,8,3]
arr max_val(arr)
9
4. Add method: reverse_linkedlist
- original list: A -> B -> C
- reversed list: C -> B -> A.
def reverse_ll(linked_list: LinkedList):
= linked_list.first_node
current_node = None
previous_node
if not current_node:
print("Empty Linked List!")
while current_node:
# A->B->C->
# 1. <-A POINT A TO PREVIOUS NODE
# 1. BUT CANT DO THAT YET, BECAUSE WONT HAVE ACCESS
# TO NEXT NODE FOR ITERATION
# 1A. SO SAVE [NEXT_NODE],
# 1B. then CURR.NEXT IS [PREV] <-A
# 2. update [PREV]
# 3. update [CURR]
=current_node.next_node # 1A. B=A.NEXT
next_node= current_node.next_node # 1B. A.NEXT=PREV
previous_node = previous_node # 2. PREV = A
current_node = next_node # 3. CURR = CURR.NEXT
current_node
return linked_list
# 'time','of','your','life'
# empty list
reverse_ll(LinkedList()) print()
= LinkedList(NodeLL("single lonely node"))
ll
reverse_ll(ll)
print_rev_all_ll(ll)
print()
= LinkedList(node0_ll)
ll
reverse_ll(ll)
print_rev_all_ll(ll)print()
#eta: 25m
#comments: definitely helped writign it down step by step
# rather than trying to do it all in my head, see the comments
Empty Linked List!
single lonely node
life
your
of
time
5. Delete Current Node Puzzle
List-Type:
LinkedList
Class
Scenario:
You have access to a node from somewhere in the middle of a classic linked list but not to the linked list itself;
You have a variable that points to an instance of Node, but you don’t have access to the LinkedList instance.
Write code that will effectively delete this node from the list.
The entire remaining list should remain complete, with only this node removed.
def delete_node_ll(current_node: NodeLL)->None:
# A -> B -> C -> D
# Delete current_node: [B]
# A -> -> C -> D
# A -> C -> D
# C.data into B.data
# D.data into C.data
# None into D.data
if not current_node.data:
print("Empty Node!")
return None
while current_node.next_node: # STOPS AT CN=D, LAST ENTERS CN=C
= current_node.next_node.data #B, C,
current_node.data # print(f"NEW_CURRENT_DATA: {current_node.data}")
=current_node.next_node
current_node= None
current_node.data # print(f"NEW_CURRENT_DATA: {current_node.data}")
return None
# classic nodes setup
=NodeLL("A")
node0_ll=NodeLL("B")
node1_ll=NodeLL("C")
node2_ll=NodeLL("D")
node3_ll=node1_ll
node0_ll.next_node=node2_ll
node1_ll.next_node=node3_ll
node2_ll.next_node= LinkedList(node0_ll)
ll print(f"Before Deleting 'B'")
print_all_ll(ll)
delete_node_ll(node1_ll)print()
print(f"After Deleting 'B'")
print_all_ll(ll)# print()
# print()
# ll = LinkedList(node0_ll)
# reverse_ll(ll)
# print_rev_all_ll(ll)
# print()
Before Deleting 'B'
A
B
C
D
After Deleting 'B'
A
C
D
None