对两数相加使用的技巧进行简单分析

众所周知,LeetCode 第 2 题“两数相加”是程序员刷题绕不开的基础题。这次我不想讲算法思路,而想聊聊其中某些解法里一个非常有趣的技巧——哑节点(Dummy Node)

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode dummy(0);          // 哑节点,栈上分配
        ListNode* tail = &dummy;
        int carry = 0;
        while (l1 || l2 || carry) {
            int sum = carry;
            if (l1) { sum += l1->val; l1 = l1->next; }
            if (l2) { sum += l2->val; l2 = l2->next; }
            tail->next = new ListNode(sum % 10);
            tail = tail->next;
            carry = sum / 10;
        }
        return dummy.next;
    }
};

在做这道题的时候我觉得最有趣的就是哑节点的引入,看了一下网上的定义
哑节点的核心思想是:在真正的头节点前“粘”一个永远不变的占位节点。它不存储任何有效数据,却把原来的头节点从“特殊节点”降级为“普通节点”,从而消除了对头节点的边界判断。
它把实际的头节点(也就是没有一个指向它的节点)定义了一个指向它的占位用的节点。这个节点本身不起任何作用,只是把原本的头节点从特殊节点转为一般节点。

这能带来哪些好处呢?

我给出两个不用哑节点的实现

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = nullptr;   // 结果链表的头
        ListNode* tail = nullptr;   // 结果链表的尾
        int carry = 0;
        while (l1 || l2 || carry) {
            int sum = carry;
            if (l1) { sum += l1->val; l1 = l1->next; }
            if (l2) { sum += l2->val; l2 = l2->next; }

            ListNode* node = new ListNode(sum % 10);
            if (head == nullptr) {   // 第一个节点
                head = node;
                tail = node;
            } else {                 // 后续节点
                tail->next = node;
                tail = node;
            }
            carry = sum / 10;
        }
        return head;
    }
};

这个版本需要在循环中判断 head == nullptr,代码多出分支,且容易遗忘更新 tail。

第二个是递归的,也是这道题本来希望考察的

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2, int carry = 0) {
        if (!l1 && !l2 && !carry) return nullptr;
        int sum = carry;
        if (l1) sum += l1->val;
        if (l2) sum += l2->val;
        ListNode* node = new ListNode(sum % 10);
        node->next = addTwoNumbers(
            l1 ? l1->next : nullptr,
            l2 ? l2->next : nullptr,
            sum / 10
        );
        return node;
    }
};

递归虽然简洁,但会占用栈空间,当链表较长时存在栈溢出风险。

而哑节点带来的好处有
1、避免了头节点的边界判断
2、局部变量自动销毁

posted @ 2026-08-19 14:17  丸子球球  阅读(2)  评论(0)    收藏  举报