跪拜 Guibai
← Back to the summary

The Dummy Node Trick That Makes Merging Sorted Linked Lists Trivial

image.png

1. Problem Introduction

Problem: Merge two ascending linked lists into a new ascending linked list and return it. The new linked list is formed by splicing together all nodes of the given two linked lists. Input: Two sorted singly linked lists list1, list2 Output: The head node of the merged sorted linked list

Core Difficulties

  1. Linked list nodes can only be traversed in one direction without backtracking, requiring continuous maintenance of the current splicing position;
  2. The two linked lists have inconsistent lengths; after the loop ends, the remaining untraversed nodes need to be spliced;
  3. Edge case handling: one of the linked lists is empty, or both are empty;
  4. Avoid special logic for handling the head node to write concise and unified code.

Optimal Solution

Dummy Node Iteration Method


2. Core Algorithm Idea (Dummy Node Concept)

1. Why Use a Dummy Node?

If a dummy node is not used, you need to first determine the minimum of list1[0] and list2[0] as the head of the result list. The subsequent loop logic and the head node judgment logic become disjointed, leading to numerous if branches in the code and poor readability.

The dummy node dummy is an empty node with no business value, always serving as a fixed starting point:

  1. Use pointer cur to point to dummy, all nodes are uniformly attached to cur.next;
  2. During traversal, only compare the current node values of list1 and list2, attaching the smaller node after cur;
  3. After traversal, dummy.next is the real head node of the merged linked list, which can be returned directly without additional head judgment.

2. Complete Process Steps

  1. Create a dummy head node dummy, define a working pointer cur = dummy;
  2. Loop: When both list1 and list2 still have nodes, compare their node values, attach the smaller node to cur.next, and simultaneously move the corresponding linked list pointer and the working pointer cur;
  3. After the loop ends, one of the linked lists must have been fully traversed. Attach the entire remaining linked list directly after cur;
  4. Return dummy.next, discarding the meaningless dummy head node.

3. Your Handwritten Complete Code (Line-by-Line Breakdown)

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    // 1. Create dummy head node
    const dummy=new ListNode();
    // 2. cur is the working pointer, initially pointing to the dummy head
    let cur=dummy;

    // 3. When both linked lists have nodes, loop to compare and merge
    while(list1&&list2){
        if(list1.val<list2.val){
            cur.next=list1;
            list1=list1.next
            cur=cur.next;
        }else {
            cur.next=list2;
            list2=list2.next;
            cur=cur.next;
        }
    }

    // 4. Traverse and splice remaining nodes of list1
    while(list1!==null){
        cur.next=list1;
        list1=list1.next;
        cur=cur.next;
    }
    // 5. Traverse and splice remaining nodes of list2
    while(list2!=null) {
        cur.next=list2;
        list2=list2.next;
        cur=cur.next;
    }

    // 6. Return the next of the dummy node, which is the real linked list head
    return dummy.next;
};

4. In-Depth Line-by-Line Analysis

1. Dummy Head Node Initialization

const dummy=new ListNode();
let cur=dummy;

2. Core Merge Loop while(list1&&list2)

The loop condition list1 && list2 means: Only when both linked lists currently have nodes do we need to compare their sizes.

if(list1.val<list2.val){
    cur.next=list1; // Attach the smaller list1 node after cur
    list1=list1.next; // Move list1 pointer backward for the next round of comparison
    cur=cur.next; // Move working pointer backward to point to the new end of the list
}else {
    cur.next=list2;
    list2=list2.next;
    cur=cur.next;
}

Three fixed steps:

  1. Assign the smaller node to cur.next to complete the attachment;
  2. Move the original linked list pointer list1/list2 one step forward;
  3. Move the working pointer cur synchronously backward, ensuring cur is always at the tail of the linked list.

3. Two While Loops for Splicing Remaining Nodes

while(list1!==null){cur.next=list1;list1=list1.next;cur=cur.next;}
while(list2!=null) {cur.next=list2;list2=list2.next;cur=cur.next;}

4. Return Result return dummy.next

The dummy head node is only a temporary placeholder. dummy.next is the first node with business value in the merged linked list.


5. Comparison of Two Remaining Node Handling Approaches

Approach 1: Your Node-by-Node Traversal (Beginner-Friendly, Code in This Article)

while(list1!==null){
    cur.next=list1;
    list1=list1.next;
    cur=cur.next;
}
while(list2!=null) {
    cur.next=list2;
    list2=list2.next;
    cur=cur.next;
}

✅ Pros: Fully demonstrates the pointer movement process, suitable for beginners learning linked lists and understanding singly linked structures; ❌ Cons: More lines of code, contains repetitive loop logic.

Approach 2: One-Line Direct Attachment (Concise Standard for Interviews)

cur.next = list1 ? list1 : list2;

✅ Pros: Done in one line, no loop, concise and efficient, common in industrial code; 💡 Principle: A linked list node's next carries the entire subsequent chain. You only need to attach the head node, and all following nodes automatically follow.

Concise Complete Code (Recommended for Interviews)

var mergeTwoLists = function(list1, list2) {
    const dummy = new ListNode();
    let cur = dummy;
    while(list1 && list2) {
        if(list1.val < list2.val) {
            cur.next = list1;
            list1 = list1.next;
        } else {
            cur.next = list2;
            list2 = list2.next;
        }
        cur = cur.next;
    }
    // Directly attach the entire remaining linked list
    cur.next = list1 || list2;
    return dummy.next;
};

6. Analysis of Your Code's Strengths and Weaknesses

✅ Code Strengths

  1. Clear Logical Layering: The main loop merges the two linked lists, and two subsequent loops complete the remaining nodes. The flow is clear at a glance;
  2. Standard Pointer Operations: After each node attachment, the list pointer and working pointer cur are moved synchronously, preventing broken chains or infinite loops;
  3. Standard Use of Dummy Head Node: Perfectly avoids special judgment for the head node, a universal template for linked list problems;
  4. No Null Pointer Risk: All loops perform non-null checks, preventing reads of null.val or null.next.

⚠️ Optimizable Points

  1. Remaining linked lists do not need loop traversal; they can be attached in one line to simplify the code;
  2. The cur=cur.next statement is repeated inside the branches and can be extracted outside the if-else to reduce redundant code:
while(list1&&list2){
    if(list1.val<list2.val){
        cur.next=list1;
        list1=list1.next
    }else {
        cur.next=list2;
        list2=list2.next;
    }
    cur=cur.next; // Extracted outside the branch, written only once
}

7. High-Frequency Interview Q&A Summary

Q1: What is the role of the dummy head node? What problems arise if not used?

A: It unifies the node attachment logic for the head, middle, and tail of the linked list. Without a dummy node, extra logic is needed to determine which of the two linked list heads is smaller to serve as the result head, leading to redundant code branches and error-prone edge cases.

Q2: Why is the time complexity O(m+n)?

A: Each node in the two linked lists is only read and attached once, with no repeated traversal. The dummy node only occupies constant space, resulting in O(1) space complexity.

Q3: How to choose between the recursive solution and the iterative (dummy) solution?

A: Recursive code is shorter but involves a call stack, which can overflow for very long linked lists. In interviews, it's better to write the iterative dummy node solution first, as it offers better stability and is more recognized by interviewers.

Q4: Why not splice both linked lists after the loop ends?

A: The termination condition of the while(list1 && list2) loop is that one of the linked lists has been fully traversed. Only one linked list has remaining nodes, so you just need to attach one or the other.


8. Problem-Solving Summary

Merging two sorted linked lists is a must-practice template problem for getting started with singly linked lists. Master two core concepts:

  1. Dummy Node: Solves the special handling problem of the linked list head, applicable to most linked list addition, deletion, and merging problems;
  2. Two-Pointer Synchronous Traversal: Merging two ordered structures is the foundational idea behind merge sort;
  3. Direct Attachment of Remaining Chain: Linked lists are chain-stored structures; there is no need to copy nodes one by one, just attach the entire remaining chain directly.

Your handwritten node-by-node traversal approach is very suitable for beginners to solidify pointer fundamentals. Once proficient, you can switch to the one-line attachment to streamline the code. Both approaches pass all test cases.