Linked Lists Questions Part 1 ================================ 152. (LC 21) Merge Two Sorted Lists ----------------------------------------- `21. Merge Two Sorted Lists `_ Easy | **Keys (iterative approach)** | -put the merged list into a new LL | -use tail = dummy (as iterator for the new list and making dummy point to the head of the new list) | -consider that l1, l2 can be of different len :: ### Solution 1 def mergeList(l1, l2): dummy = ListNode() tail = dummy #solution for making an empty node and making dummy.next point to newList head in one go while l1 and l2: if l1.val < l2.val: tail.next = l1 l1 = l1.next #we are given list HEADS, so we assign new head for l1 else: tail.next = l2 l2 = l2.next tail = tail.next #** if l1: #if l1, l2 are of different sizes, stick the remainder of longer L to our new LL tail.next = l1 if l2: tail.next = l2 return dummy.next ### Solution 1-2 (Iterative) # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def mergeTwoLists(self, list1: ListNode, list2: ListNode) -> ListNode: dummy = node = ListNode() while list1 and list2: if list1.val < list2.val: node.next = list1 list1 = list1.next else: node.next = list2 list2 = list2.next node = node.next node.next = list1 or list2 return dummy.next | **Iterative explained** | dummy = node = ListNode() | Or | dummy = current_node | dummy = tail | Empty node for list start and current node that we will be moving. | return dummy.next | Because we are to return the head of the new merged list, if dummy.next is the head, | then dummy must be an empty node. | list1 = list1.next | Because initially we are given list1, list2 <>. | So list1 represents not the whole list, but list1's head node. | So by list1=list1.next we set a different head for the list. | node.next = list1 or list2 | Means the remaining of either list1 or list2. | I.e. for the situation when e.g.: | L1=1>2>3 | L2=1>4>5>6>7 | When we run out of nodes in L1, we just add the remainder of L2 to the answer List. .. admonition:: Python x = a or b returns a if bool(a) evaluates True, else it evaluates b. Has the effect of returning the first item that evaluates True, or the last item (even if it evaluates to False). Equivalent to: determination = arg_1 if arg_1 else arg_2 if arg_2 else 'no arguments given!' **Solution 2 (Recursive)** :: # V1 [10] class Solution: def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: if not list1: return list2 if not list2: return list1 lil, big = (list1, list2) if list1.val < list2.val else (list2, list1) lil.next = self.mergeTwoLists(lil.next, big) return lil # V2 [7] class Solution: def mergeTwoLists( self, list1: Optional[ListNode], list2: Optional[ListNode] ) -> Optional[ListNode]: if list1 is None or list2 is None: return list1 or list2 if list1.val <= list2.val: list1.next = self.mergeTwoLists(list1.next, list2) return list1 else: list2.next = self.mergeTwoLists(list1, list2.next) return list2 Iterative my versions:: ### Iterative my V1 (LC accepted 92,71%) def merge(l1, l2): dummy = ListNode() cur = dummy while l1 or l2: if not l1: cur.next = l2 l2 = l2.next elif not l2: cur.next = l1 l1 = l1.next elif l1.val < l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next #<========* return dummy.next | #*Remember that with e.g. cur.next=l2 we don't yet move the node, just the pointer. | cur.next=l2 | D---->L2 | cur cur.next | cur=cur.next | D---->L2 | cur :: ### My V2 iterative (LC accepted 75, 88) class Solution: def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: if not list1 and not list2: return None if not list1: return list2 if not list2: return list1 dummy = ListNode() l3 = ListNode() dummy.next = l3 while list1 and list2: if list1.val < list2.val: l3.val = list1.val list1 = list1.next elif list2.val <= list1.val: l3.val = list2.val list2 = list2.next if list1 and list2: #then making a new empty node l3.next = ListNode() l3 = l3.next if list1 or list2: l3.next = list1 if list1 else list2 return dummy.next 153. (LC 206) Reverse Linked List -------------------------------------- `LC 206. Reverse Linked List `_ Easy | **ITERATIVE** | **Keys:** | -prev=None | Loop: | -use temp | - | -shift prev/cur/temp | -return prev because that's the new head (while current is pointing to None) Solution [:ref:`10 `]:: # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def reverseList(self, head: ListNode) -> ListNode: prev, curr = None, head while curr: temp = curr.next curr.next = prev prev = curr curr = temp return prev | The above is the iterative approach, T O(n) M O(1). | There is also recursive approach, but it is T O(n), M O(n). **RECURSIVE** :: ### LC accepted 70, 60. class Solution: def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: if not head or not head.next: return head new_head = self.reverseList(head.next) #1 head.next.next = head #2 head.next = None return new_head #3 | 1->2->3->4 | #1 | Calling recursively on the sublist 2->3->4 :: # 1 -> 2->3->4 # h h.n | #2 | After calling on the subproblem, what we are left to do is point 2 to 1, point 1 to None. :: # 1 -> 2->3->4 # h h.n # None <- 1 <- 2 | 2 = head.next | 1 = head | To point 2 to 1: 2.next = 1, so head.next.next = head | #3 | Because we need to return the head of the reversed LL. | We store the call to the function in a variable new_head, means it will store | the return value of the function when no subproblems left, when no head.next, | 1>2>3>4, head=4, no head.next, so returns head, 4. And starts working on the call stack, | the actual reversing None<3<4, None<2<3<4, None<1<2<3<4. 154. (LC 92) Reverse Linked List II --------------------------------------- `92. Reverse Linked List II `_ | Medium | Other names: Reverse sublist, reverse between | **Hooks:** | -left, right are integers, not nodes. | -So you reach the node at left with iteration. | -Don't break the sublist from the main list. Keep the node before left pointing to | the node at left. :: # Visualization # 1 -> 2 -> 3 -> 4 -> 5 # L R # prev<-| # 1 -> 2 <- 3 <- 4 X 5 # |->prev # 1 4 -> 3 -> 2 X 5 # |--------------^ # R L # 1 -> 4 -> 3 -> 2 -> 5 | After reversing the sublist between L-R: | (L->5) point the L node to the node immediately after R | (1->R) Node immediately before L point to R. **Solution** [:ref:`10 `] :: # Python3 class Solution: def reverseBetween( self, head: Optional[ListNode], left: int, right: int ) -> Optional[ListNode]: dummy = ListNode(0, head) # 1) reach node at position "left" leftPrev, cur = dummy, head for i in range(left - 1): leftPrev, cur = cur, cur.next # Now cur="left", leftPrev="node before left" # 2) reverse from left to right prev = None for i in range(right - left + 1): tmpNext = cur.next cur.next = prev prev, cur = cur, tmpNext # 3) Update pointers leftPrev.next.next = cur # LP.next.next means pointing L.next to: cur which is node after "right" leftPrev.next = prev # prev is "right" return dummy.next | **Explained** | 1) Reach node at L. | -dummy points to the head | -leftprev, with it we keep track of the node immediately before L | (after reaching L, we save it and not move it in the next step) | 2) Reverse sublist L-R (iterating till R). | Just normal reverse. | temp = curr.next | curr.next = prev | prev = curr | curr = temp | 3) Connect the reversed sublist L-R to the main list. | L->cur (point L to node after R, which is stored in cur) | leftPrev -> R (node before L point to R, R is stored in prev) | | Connecting: | LeftPrev.next.next references the node at left because we didn't disconnect the list. | Even after the reversal the node before left still points to the node at left. :: # 1>2>3>4>5 # l r # 1 4>3>2> # |-----^ # And 4 still points to 5. # 1 4>3>2>N 5 # |-------^ | My rewrite | (mistakes: we don't ever break any pointers of nodes, i.e. pointing them to None: | # beforeL.next = None <==Nope | # cur.next = None <==Nope | ) (LC accepted) :: # Python3 class Solution: def reverseBetween(self, head: Optional[ListNode], L: int, R: int) -> Optional[ListNode]: dummy = ListNode(0, head) beforeL = dummy cur=head for _ in range(L-1): beforeL=cur cur=cur.next #reverse prev=None for _ in range(R-L+1): tmp=cur.next cur.next=prev prev=cur cur=tmp beforeL.next.next=cur beforeL.next=prev return dummy.next 155. (LC 141) Linked List Cycle ------------------------------------ `141. Linked List Cycle `_ Easy :: # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None Solution [:ref:`10 `]:: class Solution: def hasCycle(self, head: ListNode) -> bool: slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: return True return False My V (LC accepted 60, 90):: class Solution: def hasCycle(self, head: Optional[ListNode]) -> bool: if not head or not head.next: return False slow = head.next fast = head.next.next while slow != fast: if not fast or not fast.next: break slow = slow.next fast = fast.next.next return slow == fast C++ ^^^^^^ .. code-block:: cpp // My V (LC accepted 40, 20) class Solution { public: bool hasCycle(ListNode *head) { if(!head || !head->next) return false; ListNode* slow = head->next; ListNode* fast = head->next->next; while(slow != fast){ if(!fast || !fast->next) break; slow = slow->next; fast = fast->next->next; } return slow == fast; } }; **Code for two methods: Hash and Floyd's** [:ref:`14 `] .. code-block:: cpp #include #include using namespace std; struct Node{ int data; Node* next; Node(int d=0): data(d), next(nullptr){} //constructor }; //PRINT LL void print_ll(Node* head){ Node *n = head; //dummy Node not to advance the head pointer while(n){ cout << n->data << "->"; n = n->next; } cout << "NULL\n"; } //HASH MAP bool detect_loop_map(Node *head){ unordered_map visited; Node *cur = head; while(cur){ if(visited[cur]) return true; visited[cur] = true; cur = cur->next; } return false; } //FLOYD'S TWO POINTERS SLOW, FAST bool detect_loop_floyd(Node *head){ Node * slow = head; Node *fast = head; while(slow && fast && fast->next){ slow = slow->next; fast = fast->next->next; if(slow == fast) return true; } return false; } void create_loop(Node *head){ Node *cur = head; while(cur->next){ //reach the last node cur = cur->next; } cur->next = head->next; //point last node to the node after head } int main(){ //1->2->3->4->5->6->nullptr Node n1(1), n2(2), n3(3), n4(4), n5(5), n6(6); n1.next = &n2; n2.next = &n3; n3.next = &n4; n4.next = &n5; n5.next = &n6; Node *head = &n1; //NO LOOP, check with 2 methods bool hasloop = detect_loop_map(head); cout << boolalpha << hasloop << endl; //false hasloop = detect_loop_floyd(head); cout << hasloop << endl; //false //CREATE LOOP and check again with 2 methods create_loop(head); hasloop = detect_loop_map(head); cout << hasloop << endl; //true hasloop = detect_loop_floyd(head); cout << hasloop << endl; //true } 156. (LC 142) Linked List Cycle II -------------------------------------- `142. Linked List Cycle II `_ Medium **Note on the task:** From the problem description you might think they want us to return the index of the node from which the cycle starts: Output: tail connects to node index 1. But from the code boilerplate we see that we are to return the node itself. +If no cycle, return None. | **Solution 1** [:ref:`2 `] | **Keys:** | -Is there a cycle | use S, F. Start both from head. While F.next and F.next.next loop till they meet. | -if S and F met, from that node: | 2nd loop. Start S at head, F at where they met. Move each +1. | Where they meet again is the start of the cycle. :: class Solution: def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: fast = slow = head while fast and fast.next and fast.next.next: slow, fast = slow.next, fast.next.next if slow is fast: # There is a cycle, so find cycle start slow = head while slow is not fast: slow, fast = slow.next, fast.next # both advance +1 return slow # where S and F meet return None # no cycle **Solution 2** :: class Solution: def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: fast = slow = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: break # If there is no cycle if not fast or not fast.next: return None ans = head while ans != slow: ans = ans.next slow = slow.next return ans | **Explained** | -Find if it is a cycle (find node where fast and slow meet). | -separate block to return None if there is no cycle | -Finding cycle start. Set two pointers | 1)where fast and slow met, e.g. take slow | 2)set new pointer to the head of the entire list, e.g. ans | Move both slow and ans with the same speed. When they meet, that's your starting | node of the cycle. (There is a "mathematical" reason why it is so.) :: # a a1,s4 # s,f s1,f2 f1,s2 s3,f3 # 3-> 2 -> 0 -> -4 # ^--------------| :: ### My V (LC accepted 70, 60%) class Solution: def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: if not head or not head.next: return None slow, fast = head, head while True: try: slow = slow.next fast = fast.next.next if slow == fast: break except: return None ss1 = slow ss2 = head while ss1 != ss2: ss1 = ss1.next ss2 = ss2.next return ss1 157. (LC 160) Intersection of Two Linked Lists ------------------------------------------------- | `LC 160. Intersection of Two Linked Lists `_ | Easy | (Other names: Test for overlapping lists (lists are without cycle)) | **Notes on task interpretation** [:ref:`2 `]: | (Practical usage - reducing memory footprint.) | Intersection - node that is common to two lists. | Lists overlap if they have the same tail node. | (Also, once the lists converge at a node,they cannot diverge at a later node.) | O(m + n) time, space O(1) | **Solution 1** [:ref:`10 `] :: # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def getIntersectionNode( self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: l1, l2 = headA, headB while l1 != l2: l1 = l1.next if l1 else headB #IMPORTANT if l1, not if l1.next l2 = l2.next if l2 else headA return l1 ### My V (LC accepted 40,50%) class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: p1=headA p2=headB while p1 != p2: if not p1: #when p1=None p1 = headB #again, set p1 to headB, not p1.next elif not p2: p2 = headA else: p1=p1.next p2=p2.next return p1 | **Keys:** | -two pointers | -increment +1 | -achieving the len difference of lists: | switching to the head of the list when pointer=None, i.e. reaches list end. | **Why it does not make an infinite loop:** | Because we allow pointers to slide off the respective list to None value before | pointing it to the head of the opposite list. Then at some point both l1, l2 will | be None, i.e. the same, even if lists don't intersect. | We point l1, l2 to opposite heads just once. | After that, they are pointed to the same (their own) heads. If no intersection, they | will eventually run off the lists into None. :: # _->_-> # _->_->_ # _->_->_-> # 1)We set p1, p2 to the heads of the respective nodes. # p1_->_-> # _->_->_ # p2_->_->_-> # 2)Increment each +=1, until one p reaches the end of its respective end # (p2 didn't yet reach the list end). # _->_-> # _->p2_->p1_ # _->_->_-> # 3)Set that point to the head of the OPPOSITE list. # _->_-> # _->_->p2_ # p1_->_->_-> # 4)When p2 reached the list end, set it to the head of the opposite list too. # p2_->_-> # _->_->_ # _->p1_->_-> # 5)Loop goes on till node at p1 and p2 is the same # _->_-> # p1,p2_->_->_ # _->_->_-> | **Alternative solution 1** (uses memory O(n)): | 1)Hash set. Add nodes in L1 to a hash set. | 2)Iterate L2 nodes. If node in hash, you have the intersect node. | **Alternative solution 2** | Similar, more verbose alternative to Solution 1 (the same efficiency and main logic as Solution 1). | 1)Recognize that L1,L2 can have different lengths (as above, len 5, len 6). | 2)Start p1 at the head of a shorter list. p2 at the head of the longer list. | 3)Increment only p2 by the diff in len of the two lists. | 4)Begin the main alg. Compare nodes at p1, p2. if not the same: +=1. | If the same, that's your intersect node. 158. Intersection of Two Linked Lists - lists may have cycles ------------------------------------------------------------------ (Other names: Test for overlapping lists (lists may have cycles)) The same as the previous task. But this time one, both or non of the lists may have a cycle. :: # A->B # V # C->D->E->F # ^--------| Both C and D are acceptable answers. | **CASE ANALYSIS** | =>Test each LL for cycles | (use 142. Linked List Cycle II, returns Node cycle start, or None if no cycle) | 1)Neither list is cyclic -> | Then just use solution for '160 LC (157 My numbering). Intersection of Two Linked Lists (lists don't have cycles)' | 2)One is cyclic, the other is not. Then they cannot overlap. We are done. | 3)Both are cyclic. | Subcases: :: # 1/ cycles are disjoint. No overlap. # 2/ Merge node is before the cycle start # A->B # V # C->D->E->F # ^--| # 3/ Merge node is in the cycle # A->B # V # C->D->E->F # ^--------| **Solution 1** :: def detectCycle(head: Optional[ListNode]) -> Optional[ListNode]: fast = slow = head while fast and fast.next and fast.next.next: slow, fast = slow.next, fast.next.next if slow is fast: # There is a cycle, so find cycle start slow = head while slow is not fast: slow, fast = slow.next, fast.next # both advance +1 return slow # where S and F meet return None def getIntersectionNode(headA: ListNode, headB: ListNode) -> Optional[ListNode]: l1, l2 = headA, headB while l1 != l2: l1 = l1.next if l1 else headB l2 = l2.next if l2 else headA return l1 def overlapping_lists(L1, L2): # Find cycle starts if any root1, root2 = detectCycle(L1), detectCycle(L2) if not root1 and not root2: # Both L1, L2 no cycle return getIntersectionNode(L1, L2) # Note, the func assumes they do overlap elif (root1 and not root2) or (not root1 and root2): # Only 1 list has cycle, so no overlap return None # Both have cycles # Test if they are not disjoint # If overlap: Starting at the cycle start of L2, you should meet the cycle start of L1. temp = root2 while True: temp = temp.next if temp is root1 or temp is root2: break if temp is not root1: return None # Disjoint cycles ### One L has cycle # Helper func. Distance from head to intersect node, i.e. unique stem_length def distance(a, b): dis = 0 while a is not b: a = a.next dis += 1 return dis # Overlap before cycle start stem1_length, stem2_length = distance(L1, root1), distance(L2, root2) if stem1_length > stem2_length: L2, L1 = L1, L2 root1, root2 = root2, root1 # List with longer unique stem, move it till both stems are the same dist from merge node **1 for _ in range(abs(stem1_length - stem2_length)): L2 = L2.next # Takes care of both: 1)when overlap is before cycle (overlap=L1==L2) # 2)And the subcase when overlap is within the cycle (overlap=root1) **2 while L1 is not L2 and L1 is not root1 and L2 is not root2: L1, L2 = L1.next, L2.next return L1 if L1 is L2 else root1 #**1:: # L2 # (L1) A->B # V # (L2) L1 C->D->E->F (At E: root1, root2) # ^--| Pointers L2 at B and L1 at C are now the same distance from D (merge node). Now we just have to move them .next till L1=L2 | #**2 | Merge node is in the cycle. | A:root1, C:root2, if stem1_lenght > stem2_length, roots swap, making A:root2 :: # A->B # V # C->D->E->F # ^--------| Last comments [:ref:`2 `]: If Ll == L2 before reaching root1, it means the overlap first occurs before the cycle starts; otherwise, the first overlapping node is not unique, we can return any node on the cycle. 159. (LC 143) Reorder List ------------------------------- `143. Reorder List `_ Medium | **Quick notes:** | -to find halves, iter1=head, iter2=head.next, while iter2 and iter2.next | -you are more likely to need | tmp = cur.next | than | tmp = cur | If you will be modifying cur later on. :: ### Solution 1 (My rewrite V1, LC accepted: Memory beats 94%, Time 40%) class Solution: def reorderList(self, head: Optional[ListNode]) -> None: """ Do not return anything, modify head in-place. """ slow=head fast=head.next #find 2 halves of the list while fast and fast.next: slow=slow.next fast=fast.next.next #reverse 2nd half cur=slow.next # slow=B, slow.next=cur=C (start reversing at C) slow.next=None # A>B C>D (breaking B>C link, B>None) prev=None while cur: tmp=cur.next cur.next=prev prev=cur cur=tmp #merge half1 and half2 #1 cur1=head #head of half1 cur2=prev #head of half2 while cur2: tmp1=cur1.next tmp2=cur2.next cur1.next=cur2 cur2.next=tmp1 cur1=tmp1 cur2=tmp2 # #1 Alternative merge # iter1 = head # iter2 = prev # while iter2: # nex = iter1.next # iter1.next = iter2 # iter1 = iter2 # iter2 = nex | **Explained** | # Overview | Linked List | A>B>C>D | Where half would be. | A B | C D (For odd len list also: A B C | D E) | | ###1 Determine the half of the list. Use slow, fast pointers. | (Note, they begin at different positions.) :: # A B C D # S F # # A B C D # S F | ###2 We need to REVERSE THE 2ND HALF OF THE LIST. | The head of the 2nd list is at S.next | second = slow.next | The actual breaking of the original list consists in pointing the last node of the | first half to None. I.e. S.next=None | prev = slow.next = None | | Recall how we reverse a LL (we use exactly the same code): | prev, curr = None, head | while curr: | temp = curr.next | curr.next = prev | prev = curr | curr = temp | return prev ###3 THE MERGE:: # After reversing the 2nd half we have: # A>B CB CD>B C # V V | # Merging in code | first, second = head, prev #prev is the last node in list (head of the 2nd half) | while second: | tmp1, tmp2 = first.next, second.next | first.next = second | second.next = tmp1 | first, second = tmp1, tmp2 | | first, second = head, prev | Assign first and second to list heads. | first=A, second=D | | while second: | Loop while 2nd list has no nodes. | | tmp1, tmp2 = first.next, second.next | Put nodes after heads to temp vars. | tmp1=B, tmp2=C | | first.next = second | second.next = tmp1 | Point head of 1st list to head of 2nd list. | Point head of L2 to memorized L1.next. | | first, second = tmp1, tmp2 | Reassign heads of L1, L2. **Solution 1** formal :: class Solution: def reorderList(self, head: ListNode) -> None: # find middle slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next # reverse second half second = slow.next #the starting point/node for the list 2nd half (where slow sopped + 1) prev = slow.next = None #break list into 2 halves (pointing to None breaks the list) while second: tmp = second.next second.next = prev prev = second second = tmp # merge two halves first, second = head, prev #prev is the last node in list (head of the 2nd half) while second: tmp1, tmp2 = first.next, second.next first.next = second second.next = tmp1 first, second = tmp1, tmp2 160. (LC 237) Delete Node in a Linked List ---------------------------------------------- `237. Delete Node in a Linked List `_ Medium | The input node is guaranteed not to be the tail node. | You are given the pointer to a node to delete. | -What's the trouble? | Deleting a node usually requires modifying its predecessor's next pointer and | the only way to get to the predecessor is to traverse the list from head, | which requires O(n) time. | -The trick. | Delete given node's successor. Copy next node's data into the current node. Delete next node. | This takes O(1). :: class Solution: def deleteNode(self, node_to_delete): """ :type node: ListNode :rtype: void Do not return anything, modify node in-place instead. """ node_to_delete.val = node_to_delete.next.val node_to_delete.next = node_to_delete.next.next # OR # def deleteNode(node): # node.val = node.next.val # node.next = node.next.next