Hey there, fellow coders! Today, we're going to dive into a classic problem that often pops up in technical interviews and real-world programming scenarios: merging two sorted linked lists. Don't worry if it sounds intimidating at first – by the end of this post, you'll be merging lists like a pro!
The Problem
Imagine you have two linked lists, both already sorted in ascending order. Your task is to merge them into a single sorted linked list. Sounds simple enough, right? Well, the trick is to do it efficiently and elegantly.
For example, let's say we have two lists:
List 1: 1 -> 3 -> 5 -> 7 List 2: 2 -> 4 -> 6 -> 8
Our goal is to merge them into:
Merged List: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
The Approach
The beauty of this problem is that we can leverage the fact that both input lists are already sorted. This allows us to use a straightforward, single-pass approach. Here's the game plan:
- Create a dummy node as the start of our merged list.
- Use two pointers to traverse both input lists simultaneously.
- Compare the values of the current nodes in both lists.
- Add the smaller value to our merged list and move the pointer for that list.
- Repeat steps 3-4 until we reach the end of one of the lists.
- Append any remaining nodes from the non-empty list to our merged list.
The Code
Let's implement this solution in Python. First, we'll define our ListNode
class:
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
Now, here's the function to merge two sorted linked lists:
def mergeTwoLists(l1: ListNode, l2: ListNode) -> ListNode: # Create a dummy node to start our merged list dummy = ListNode(0) current = dummy # Traverse both lists simultaneously while l1 and l2: if l1.val <= l2.val: current.next = l1 l1 = l1.next else: current.next = l2 l2 = l2.next current = current.next # Append any remaining nodes if l1: current.next = l1 if l2: current.next = l2 # Return the merged list (excluding the dummy node) return dummy.next
Breaking It Down
Let's walk through this code step by step:
-
We start by creating a dummy node. This is a common trick in linked list problems to avoid having to handle the head of the list as a special case.
-
We use a
current
pointer to keep track of where we are in the merged list as we build it. -
The
while
loop continues as long as both input lists have nodes left to process. -
Inside the loop, we compare the values of the current nodes in both lists. The smaller value gets added to our merged list, and we move the pointer for that list forward.
-
After the loop, one of the lists might still have nodes left. We append these remaining nodes to our merged list.
-
Finally, we return
dummy.next
, which is the actual head of our merged list (remember,dummy
was just a placeholder to make our life easier).
Time and Space Complexity
- Time Complexity: O(n + m), where n and m are the lengths of the input lists. We traverse each node exactly once.
- Space Complexity: O(1), as we're only using a constant amount of extra space to keep track of our pointers. The merged list doesn't count as extra space since it's our required output.
A Real-World Example
Imagine you're building a music streaming app. You have two playlists, both sorted by song release date. You want to merge these playlists while maintaining the chronological order. This algorithm would be perfect for that!
Tips for Interview Success
If you encounter this problem in an interview, here are some pro tips:
- Start by explaining the approach before diving into code.
- Mention the dummy node technique and why it's useful.
- Discuss the time and space complexity.
- If time allows, consider edge cases (e.g., what if one or both input lists are empty?).