[LeetCode single linked list] merge two ordered linked lists (21)

Posted by orionblue on Tue, 07 Dec 2021 18:17:49 +0100

1. Title

Merge the two ascending linked lists into a new ascending linked list and return the head node of the linked list. The new linked list is composed of all nodes of a given two linked lists.

1.1 example

  • Example 1 1 1 :
  • Input: list1 = [1, 2, 4], list2 = [1, 3, 4];
  • Output: [1, 1, 2, 3, 4, 4].

  • Example 2 2 2 :
  • Input: list1 = [], list2 = [];
  • Output: [].
  • Example 3 3 3 :
  • Input: list1 = [], list2 = [0];
  • Output: [0].

1.2 description

1.3 restrictions

  • -100 <= Node.val <= 100 ;
  • The number range of nodes of the two linked lists is [0, 50];
  • list1 and list2 are arranged in non decreasing order.

2. Solution I (iterative method)

2.1 analysis

The iterative method is very simple to solve this problem, which is used below 3 3 Three auxiliary cursor variables: cursor, cursor1 and cursor2 are used to indicate the node position in the merged linked list, the first given linked list and the second given linked list respectively during the iteration process.

2.2 answers

from typing import Optional


class ListNode:
    def __init__(self, val=0, nxt=None):
        self.val = val
        self.nxt = nxt


class Solution:
    def merge2lists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1:
            return list2
        if not list2:
            return list1
        cursor1, cursor2 = list1, list2
        if cursor1.val < cursor2.val:
            merged_list = ListNode(cursor1.val)
            cursor1 = cursor1.nxt
        else:
            merged_list = ListNode(cursor2.val)
            cursor2 = cursor2.nxt
        cursor = merged_list
        while cursor1 and cursor2:
            if cursor1.val < cursor2.val:
                cursor.nxt = ListNode(cursor1.val)
                cursor1 = cursor1.nxt
            else:
                cursor.nxt = ListNode(cursor2.val)
                cursor2 = cursor2.nxt
            cursor = cursor.nxt
        if cursor1:
            cursor.nxt = cursor1
        if cursor2:
            cursor.nxt = cursor2
        return merged_list


def main():
    node6 = ListNode(val=4)
    node5 = ListNode(val=3)
    node4 = ListNode(val=1)
    node4.nxt, node5.nxt = node5, node6
    node3 = ListNode(val=4)
    node2 = ListNode(val=2)
    node1 = ListNode(val=1)
    node1.nxt, node2.nxt = node2, node3
    list1, list2 = node1, node4
    sln = Solution()
    merged_list = sln.merge2lists(list1, list2)
    cursor = merged_list
    while cursor:
        print(cursor.val)
        cursor = cursor.nxt


if __name__ == '__main__':
    main()

It should be noted that in the above code, when each iteration of the while loop is completed, the cursor cursor needs to be moved every time except cursor1 or cursor2 cursor according to the conditions.

The test run results of the above code are as follows:

1
1
2
3
4
4

2.3 complexity

  • Time complexity: O ( n + m ) O(n+m) O(n+m), where n n n and m m m is the length of the two linked lists respectively. Because only one element of list1 and list2 will be put into the combined linked list in each loop iteration, the number of while loops will not exceed the sum of the lengths of the two linked lists. The time complexity of all other operations is constant, so the total time complexity is O ( n + m ) O(n+m) O(n+m) ;
  • Space complexity: O ( 1 ) O(1) O(1) . Only constant space is needed to store several variables.

Although the time and space complexity of the above solutions are excellent, the code is a little verbose and not elegant, because a special code segment is used to process the header node of the merged linked list. In fact, a dummy node can be used to optimize it as follows:

from typing import Optional


class ListNode:
    def __init__(self, val=0, nxt=None):
        self.val = val
        self.nxt = nxt


class Solution:
    def merge2lists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        # A dummy.next node to store the result
        dummy = ListNode(-sys.maxsize)
        cursor = dummy
        while True:
            # If any of the list gets completely empty
            # directly join all the elements of the other list
            if list1 is None:
                cursor.next = list2
                break
            if list2 is None:
                cursor.next = list1
                break
                
            # Compare the data of the lists and whichever is smaller is
            # appended to the last of the merged list and the head is changed
            if list1.val <= list2.val:
                cursor.next = list1
                list1 = list1.next
            else:
                cursor.next = list2
                list2 = list2.next
    
            # Advance the cursor
            cursor = cursor.next
    
        # Returns the head of the merged list
        return dummy.next

3. Solution II (recursive method)

3.1 analysis

In addition to the above iterative solution, the recursive solution shown below can also be used for this problem. Although the recursive solution is simpler, it is also less easy to understand.

3.2 answers

from typing import Optional


class ListNode:
    def __init__(self, val=0, nxt=None):
        self.val = val
        self.nxt = nxt


class Solution:
    def recursive_merge2lists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1:
            return list2
        if not list2:
            return list1
        if list1.val < list2.val:
            head = list1
            head.nxt = self.recursive_merge2lists(list1.nxt, list2)
        else:
            head = list2
            head.nxt = self.recursive_merge2lists(list1, list2.nxt)
        return head


def main():
    node6 = ListNode(val=4)
    node5 = ListNode(val=3)
    node4 = ListNode(val=1)
    node4.nxt, node5.nxt = node5, node6
    node3 = ListNode(val=4)
    node2 = ListNode(val=2)
    node1 = ListNode(val=1)
    node1.nxt, node2.nxt = node2, node3
    list1, list2 = node1, node4
    sln = Solution()
    merged_list = sln.recursive_merge2lists(list1, list2)
    cursor = merged_list
    while cursor:
        print(cursor.val)
        cursor = cursor.nxt


if __name__ == '__main__':
    main()

3.3 complexity

  • Time complexity: O ( n + m ) O(n+m) O(n+m), where n n n and m m m is the length of the two linked lists respectively. Because the head node of list1 or list2 will be removed every time recursion is called (until at least one linked list is empty), the method recursive_merge2lists calls each node recursively at most once. Therefore, the time complexity depends on the length of the combined linked list, i.e O ( n + m ) O(n+m) O(n+m) ;
  • Space complexity: O ( n + m ) O(n+m) O(n+m), where n n n and m m m is the length of the two linked lists respectively. Recursive call recursive_ The merge2lists method consumes stack space, which depends on the depth of recursive calls. Recursive at end of recursive call_ The merge2lists method is called at most n + m n+m n+m times, so the spatial complexity is O ( n + m ) O(n+m) O(n+m) .

Topics: Algorithm leetcode linked list