I. 两数相加
核心思路:
- 使用虚拟头节点简化结果链表的创建和边界处理。
- 用一个进位变量(carry) 记录每一位相加后的进位(初始为 0)。
- 同时遍历两个链表,每一步计算:
当前位和 = 链表1当前值 + 链表2当前值 + 进位。 - 新节点的值为
当前位和 % 10,更新进位为当前位和 / 10。 - 当其中一个链表遍历完后,继续处理另一个链表的剩余节点和进位。
- 最后若仍有进位(如 999 + 1 = 1000),需新增一个节点存储进位。
步骤拆解:
- 创建虚拟头节点
dummy和当前指针current(指向结果链表的当前尾部)。 - 初始化进位
carry = 0。 - 循环遍历两个链表,直到两个链表都遍历完且进位为 0:
- 取出两个链表当前节点的值(若节点为空则取 0)。
- 计算总和
sum = val1 + val2 + carry。 - 创建新节点,值为
sum % 10,接在current后面。 - 更新进位
carry = sum / 10。 - 移动两个链表的指针(若不为空)和
current指针。
- 返回
dummy.next(结果链表的头节点)。 - 进位处理:每一位相加后可能产生进位(0 或 1,因为最大情况是 9+9+1=19,进位为 1),进位会参与下一位的计算。循环条件包含
carry != 0,确保最后一位的进位被处理(如 999 + 1 的情况)。 - 链表长度不同的处理:当一个链表遍历完后,通过
val1 = (l1 != null) ? l1.val : 0的方式,将空节点视为 0,继续与另一个链表的剩余节点相加。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 创建虚拟头节点,简化结果链表的构建
ListNode dummy = new ListNode(0);
ListNode current = dummy; // 当前指针,指向结果链表的尾部
int carry = 0; // 进位,初始为0
// 遍历两个链表,直到都为空且没有进位
while (l1 != null || l2 != null || carry != 0) {
// 取出当前节点的值(为空则取0)
int val1 = (l1 != null) ? l1.val : 0;
int val2 = (l2 != null) ? l2.val : 0;
// 计算当前位的总和(包括进位)
int sum = val1 + val2 + carry;
// 新节点的值为总和对10取余
current.next = new ListNode(sum % 10);
// 更新进位(总和除以10)
carry = sum / 10;
// 移动指针
current = current.next;
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;
}
// 返回结果链表的头节点(跳过虚拟头节点)
return dummy.next;
}
}
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
II. 删除链表的倒数第N个结点
核心思路:
- 使用双指针法(快慢指针):让快指针比慢指针提前移动 N 步,然后两指针同步移动,当快指针到达链表尾部时,慢指针刚好指向倒数第 N 个节点的前驱节点。
- 引入虚拟头节点:简化删除头节点的边界情况(例如删除链表的第一个节点时,无需特殊处理)。
步骤拆解:
- 创建虚拟头节点
dummy,其next指向原链表头节点。 - 初始化快指针
fast和慢指针slow,均指向dummy。 - 快指针先移动 N 步(确保与慢指针拉开 N 个节点的距离)。
- 快慢指针同步移动,直到快指针的
next为null(快指针到达最后一个节点)。 - 此时慢指针的
next即为倒数第 N 个节点,通过slow.next = slow.next.next删除该节点。 - 返回
dummy.next(修改后的链表头节点)。 - 双指针的作用:快指针提前移动 N 步后,两指针同步移动,当快指针到达尾部时,慢指针恰好停在倒数第 N 个节点的前驱节点,只需一次遍历即可定位目标节点,时间效率更高。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 创建虚拟头节点,处理删除头节点的边界情况
ListNode dummy = new ListNode(0);
dummy.next = head;
// 初始化快慢指针,均指向虚拟头节点
ListNode fast = dummy;
ListNode slow = dummy;
// 快指针先移动n步,与慢指针拉开n个节点的距离
for (int i = 0; i < n; i++) {
fast = fast.next;
}
// 快慢指针同步移动,直到快指针到达最后一个节点
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 此时slow.next即为倒数第n个节点,删除它
slow.next = slow.next.next;
// 返回修改后的链表头节点(跳过虚拟头节点)
return dummy.next;
}
}
III. 两两交换链表中的节点
核心思路:
- 使用虚拟头节点(dummy node) 简化头节点交换的边界处理(例如交换前两个节点时,无需单独修改头节点)。
- 用一个前驱指针(prev) 指向需要交换的两个节点的前一个节点,便于修改指针指向。
- 每次交换
prev后面的两个节点(node1和node2),步骤如下:
prev.next指向node2(让前驱节点连接到第二个节点)。node1.next指向node2.next(第一个节点连接到第二个节点的下一个节点)。node2.next指向node1(第二个节点连接到第一个节点)。- 移动
prev到node1(作为下一组交换的前驱)。
步骤拆解:
- 重复上述过程,直到没有可交换的节点(即
prev后面不足两个节点)。 - 创建虚拟头节点
dummy,其next指向原链表头节点,prev初始指向dummy。 - 循环交换:
- 定义
node1 = prev.next,node2 = node1.next(若node1或node2为null,则退出循环)。 - 执行交换操作(修改三个指针指向)。
- 移动
prev到node1。
- 定义
- 返回
dummy.next(交换后的链表头节点)。
- 边界条件处理:循环条件
prev.next != null && prev.next.next != null确保只有当存在两个可交换的节点时才执行交换,处理了链表长度为奇数(最后一个节点不交换)或空链表的情况。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
// 创建虚拟头节点,简化头节点交换的处理
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode prev = dummy; // 前驱指针,初始指向虚拟头节点
// 循环交换,直到没有可交换的节点(prev后不足两个节点)
while (prev.next != null && prev.next.next != null) {
ListNode node1 = prev.next; // 第一个节点
ListNode node2 = prev.next.next; // 第二个节点
// 交换节点
prev.next = node2; // 前驱指向第二个节点
node1.next = node2.next; // 第一个节点指向第二个节点的下一个
node2.next = node1; // 第二个节点指向第一个节点
// 移动前驱指针到下一组的前一个节点(即当前的第一个节点)
prev = node1;
}
// 返回交换后的链表头节点
return dummy.next;
}
}
1824



被折叠的 条评论
为什么被折叠?



