力扣-2.两数之和(个人学习记录)(链表)(指针)

一、题目[1]

图片
图片

二、代码

1)C++

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode() : val(0), next(nullptr) {}
*     ListNode(int x) : val(x), next(nullptr) {}
*     ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
   ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
       ListNode* dummy = new ListNode(0); // 虚拟头节点,避免处理头节点为空的特殊边界情况
       ListNode* curr = dummy;            // 当前操作指针
       int carry=0;                       // 进位标志
       while(l1!=nullptr || l2!=nullptr || carry !=0){ // carry != 0
           int sum = carry;                            // sum = carry
           if(l1!=nullptr){
               sum += l1->val;
               l1 =l1->next;
           }
           if(l2!=nullptr){
               sum += l2->val;
               l2 =l2->next;
           }
           carry = sum/10;
           int digit = sum%10;

           curr->next = new ListNode(digit);
           curr = curr->next;
       } 
       return dummy->next;
}
};

代码分析

1)为什么要使用这两个指针?

ListNode* dummy = new ListNode(0);  // ① 在堆上创建节点,dummy指向它
ListNode* curr = dummy;             // ② curr也指向同一个地址(替身)

类比:dummy 是地基桩(不动),curr 是施工队(移动)

// 初始状态:dummy 和 curr 都指向空节点
dummy → [0] → null
curr  → [0] → null  (curr和dummy指向同一个)

// 第一次添加节点:curr干活,dummy不动
curr->next = new ListNode(7);  // 在curr后面接上新节点
curr = curr->next;             // curr前移(curr现在指向新节点7)

// 现在状态:
dummy → [0] → [7] → null
       ↑      ↑
     dummy   curr

// 第二次添加:
curr->next = new ListNode(0);  // 在7后面接0
curr = curr->next;             // curr前移

// 最终状态:
dummy → [0] → [7] → [0] → [8] → null
       ↑             ↑
     dummy         curr

区别

指针 作用 是否移动 最后返回值
dummy 锚点,永远指向链表起点 ❌ 不动 dummy->next(跳过头节点0)
curr 工人,负责接新节点 ✅ 不断后移 ❌ 不用它返回

总结

  • ListNode* dummy:永久保存链表起点的指针(相当于存档点)
    
  • ListNode* curr:临时工作指针(相当于当前位置标记)
    
  • curr = dummy:让 curr 从 dummy 的位置出发,之后 curr 可以随便跑,dummy 永远记得回家的路
    

这就是为什么叫 "虚拟头节点技巧" —— dummy 本身不存有效数据(val=0),它只是作为一个锚点,让代码不用处理"第一个节点是空"的特殊情况。[2]

2)为什么最后return dummy->next会是7,0,8,而不是7

因为返回的是链表的"头指针",而链表是通过 next 指针一串连起来的!

通过下面的内存图来观察最终状态,内存中的真实结构

堆内存中的链表节点(通过 next 指针串联):

dummy 指向的节点
    ↓
[val: 0 | next: 0x200]  ← 虚拟头节点(dummy 指向这里)
            ↓
[val: 7 | next: 0x300]  ← dummy->next 指向这里(结果的第一个节点)
            ↓
[val: 0 | next: 0x400]  
            ↓
[val: 8 | next: nullptr] ← 尾节点

关键理解:返回头节点 = 返回整个链表

当执行 return dummy->next; 时,返回的是地址 0x200(也就是存储 7 的那个节点)。

虽然只返回了一个地址,但这个节点里面藏着 next 指针(指向 0x300),而 0x300 里又藏着指向下一个节点的指针... 就像一串钥匙,第一把能拿到第二把,第二把能拿到第三把。[2]

验证代码

这样验证返回的是整个链表:

ListNode* result = sol.addTwoNumbers(l1, l2);  
// result 现在指向存储 7 的节点

// 打印 result->val → 输出 7(第一个数)
cout << result->val;  // 7

// 打印 result->next->val → 输出 0(第二个数)  
cout << result->next->val;  // 0

// 打印 result->next->next->val → 输出 8(第三个数)
cout << result->next->next->val;  // 8

// 再往后就是 nullptr 了
cout << result->next->next->next;  // 0 (nullptr)

三、链表

该题主要是用链表数据结构
图片
链表的组成单位是节点(node)对象。每个节点都包含两项数据:节点的“值”和指向下一节点的“引用”。

  • 链表的首个节点被称为“头节点”,最后一个节点被称为“尾节点”。
    
  • 尾节点指向的是“空”,它在 Java、C++ 、C和 Python中分别被记为 null、nullptr、NULL 和 None 。
    
  • 在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。
    

如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链表比数组占用更多的内存空间。[3]

参考引用

[1]力扣,两数之和:https://leetcode.cn/problems/add-two-numbers/
[2]kimi:https://www.kimi.com/
[3]Hello算法,链表:https://www.hello-algo.com/chapter_array_and_linkedlist/linked_list/

posted @ 2026-02-01 18:53  梨火旺  阅读(3)  评论(0)    收藏  举报