def delete_kth_last_node(ll: LinkedList, k: int) -> None: # case for head node removal if k == len(ll): temp = ll.head if len(ll) == 1: ll.head = None ll.rear = None else: ll.head = temp.next temp.next = None ll.length -= 1 del temp return # generic node removal ptr_end = ll.head ptr_k = ll.head # moving the ptr_end up by k nodes for _ in range(k + 1): if ptr_end is None: raise ValueError(f"Linked list contains less than {k} nodes") ptr_end = ptr_end.next # searching for the end of the linked list # ptr_k is trailing the ptr_end up by k nodes, when end pointer reaches the end, # ptr_k is k nodes away from the end while ptr_end is not None: ptr_end = ptr_end.next ptr_k = ptr_k.next # removing the required element temp = ptr_k.next ptr_k.next = temp.next temp.next = None ll.length -= 1 del temp
def sort(ll: LinkedList) -> LinkedList: ll.head = merge_sort(ll, ll.head) # reseting rear curr = ll.head while curr.next: curr = curr.next ll.rear = curr return ll
def rotate_linked_list(ll: LinkedList, k: int = 0) -> None: k = k % ll.length for _ in range(k): temp = ll.head ll.head = ll.head.next temp.next = None ll.rear.next = temp ll.rear = ll.rear.next
def reverse_inplace(ll: LinkedList) -> None: ll.rear = ll.head ptr_prev, ptr_curr, ptr_next = None, ll.head, ll.head.next # reversing the flow while ptr_curr is not None: ptr_curr.next = ptr_prev ptr_prev, ptr_curr = ptr_curr, ptr_next if ptr_next is None: break ptr_next = ptr_next.next ll.head = ptr_prev
def delete_zero_sum(linked_list: LinkedList) -> LinkedList: cumulative = 0 cumulative_sum_map = {} dummy_head = Node(0) dummy_head.next = linked_list.head linked_list.head = dummy_head # removing 0 sum nodes using the property: # x -> y -> x [values (x and y) are cumulative sums] implies the linked list # contains x -> (y - x) -> -(y - x) and hence the nodes at the end can be removed # [this property can also be used to detect multiple nodes summing up to 0] node = linked_list.head while node: cumulative += node.val if cumulative in cumulative_sum_map: cumulative_sum_map[cumulative].next = node.next cumulative_sum_map[cumulative] = node node = node.next # resetting the linked list (removing dummy head and setting rear) linked_list.head = linked_list.head.next node = linked_list.head while node: linked_list.rear = node node = node.next return linked_list
def add_node_sorted(ll: LinkedList, val: int) -> None: ll.length += 1 if not ll.head: ll.head = Node(val) ll.rear = ll.head elif val > ll.rear.val: ll.rear.next = Node(val) ll.rear = ll.rear.next else: pos = ll.head while pos.val < val: pos = pos.next temp = pos.val pos.val = val new_node = Node(temp) new_node.next = pos.next pos.next = new_node if pos == ll.rear: ll.rear = new_node
def clone(ll: LinkedList) -> LinkedList: clone_head = ll.head pos1 = ll.head pos2 = ll.head.next # duplicating all elements (by value in the linked list) # [a -> b -> c becomes a -> a -> b -> b -> c -> c] for _ in range(ll.length): pos1.next = Node(pos1.val) pos1 = pos1.next pos1.next = pos2 pos1 = pos1.next if pos2 is None: break pos2 = pos2.next # setting the clone head to the proper position clone_head = clone_head.next pos1 = ll.head # setting the random pointer of the cloned linked list # (every 2nd element in the new linked list: a -> [a] -> b -> [b] -> c -> [c]) for _ in range(ll.length - 1): pos1.next.random_ptr = pos1.random_ptr pos1 = pos1.next.next # reverting the linked list to its original form pos1 = ll.head pos2 = ll.head.next for _ in range(ll.length - 1): pos1.next = pos2.next pos2.next = pos2.next.next pos1 = pos1.next if pos2.next == None: break pos2 = pos2.next # creating the cloned linked list from the generated nodes cloned_LL = LinkedList() cloned_LL.head = clone_head cloned_LL.length = ll.length cloned_LL.rear = pos2 return cloned_LL