力扣-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/

浙公网安备 33010602011771号