最详细,最轻松的力扣(leetcode)hot 100 链表(上)讲解
这里是阳明Coding,这次带来的是关于力扣 hot 100的链表问题的做法。由于这部分内容足足有15道题目,因此按照题目的顺序拆分为上,下两部分来进行讲解。如果这边文章能够帮到你们的话,可以点赞,收藏加关注。

目录
相交链表

思考方向
假设交点前:A链表长a,B链表长b,公共部分长c。指针A路径:a + c + b。指针B路径:b + c + a。长度相等,必然在交点相遇
因此,我们只需要让两个链表的指针走到末端后,从另外一条链表开始继续仔细行走即可。
代码流程
初始化:两个指针
pa和pb分别指向两个链表的头节点headA和headB。同步移动:
每次循环,
pa和pb各走一步。如果
pa走到链表 A 的末尾,则将其重定向到链表 B 的头节点。如果
pb走到链表 B 的末尾,则将其重定向到链表 A 的头节点。相遇或结束:
如果两个链表相交,
pa和pb会在交点相遇。如果两个链表不相交,
pa和pb会同时走到null,循环结束并返回null。
代码如下
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pa = headA, pb = headB;
// 双指针遍历,若不相遇则继续
while (pa != pb) {
// pa走完A则转B,否则继续走
pa = (pa == null) ? headB : pa.next;
// pb走完B则转A,否则继续走
pb = (pb == null) ? headA : pb.next;
}
// 返回交点(若无交点则返回null)
return pa;
}
}
反转链表

思考方向
原本链表中的节点顺序是
head -> node1 -> node2 -> ... -> nodeN,我们需要将它反转成nodeN -> nodeN-1 -> ... -> node1 -> head。我们通常可以用两种方式来实现链表反转:迭代方式:通过遍历链表,将每个节点的指针反转。递归方式:将链表分成两部分,递归反转子链表,最后将当前节点的指针反转
代码流程
通过递归反转链表,逐步将每个节点的指向反转,最终返回新的头节点。
代码如下
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(head); // 调用递归函数
}
// 递归反转链表
public ListNode reverse(ListNode head) {
// 递归终止条件:空节点或最后一个节点
if (head == null || head.next == null) return head;
// 递归反转后续链表
ListNode newHead = reverse(head.next);
// 反转当前节点的指向
head.next.next = head; // 让下一个节点指向自己
head.next = null; // 断开当前节点原指向
return newHead; // 返回新的头节点
}
}
下面以示例1作为例子,花了一下示例图

上面的图说明了如何实现 5 , 4 两个节点之间指向的转换。其他节点也是同理的,可以自己画图尝试理解一下
回文链表

思考方向
通过将链表的一半反转并与另一半进行比较,可以有效判断链表是否是回文。关键在于如何找到链表的中点,并将后半部分反转
代码流程
空链表或单节点链表是回文,直接返回
true。使用快慢指针,慢指针到达中点时,快指针到达链表末尾。从中点开始,递归反转后半部分链表。
逐个比较前半部分和反转后的后半部分节点值,若有不同则返回
false。若所有节点值相同,返回true,否则返回false。
代码如下
class Solution {
public boolean isPalindrome(ListNode head) {
// 空链表或单节点必然是回文
if (head == null || head.next == null) return true;
// 快慢指针找到中点
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 反转后半部分链表
ListNode second = reverse(slow);
// 比较前后两部分
while (second != null) {
if (head.val != second.val) {
return false;
}
head = head.next;
second = second.next;
}
return true;
}
// 递归反转链表
public ListNode reverse(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = reverse(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
下面以示例一作为例子,开始演示一下,代码的实际执行流程
首先先用两个快慢指针,使得slow能够到达一半长度后面第一个位置

接下来我们要翻转链表,开始比较

环形链表

思考方向
最直接的方法是使用快慢指针。如果链表存在环,快指针(每次走两步)和慢指针(每次走一步)最终会相遇。如果链表没有环,快指针会先到达链表末尾(
fast或fast.next为null)
代码流程
特殊情况:链表为空或只有一个节点,无环,返回
false。快慢指针:初始化快指针
fast和慢指针slow,快指针每次走两步,慢指针走一步。检测环:遍历链表,若
fast和slow指针相遇,返回true(有环);若fast到达末尾,返回false(无环)。
代码如下
public class Solution {
public boolean hasCycle(ListNode head) {
// 空链表或单节点无环
if (head == null || head.next == null) return false;
// 快慢指针初始化
ListNode slow = head, fast = head;
// 遍历链表
while (fast != null && fast.next != null) {
fast = fast.next.next; // 快指针每次走两步
slow = slow.next; // 慢指针每次走一步
// 如果快慢指针相遇,说明有环
if (fast == slow) return true;
}
// 快指针到达末尾,无环
return false;
}
}
环形链表 II

思考方向
检测链表是否有环时,可以使用快慢指针方法,如果链表有环,快指针和慢指针会在环中相遇。问题的进一步扩展是如何找到环的入口节点。当快慢指针相遇时,意味着存在环。此时,令一个指针从链表头开始,另一个指针从相遇点开始。两个指针再次相遇时,即为环的入口
推导公式
L1:链表头到环入口的距离(节点数)
L2:环入口到快慢指针相遇点的距离(节点数)
C:环的长度(节点数)
设相遇时:
慢指针走了 S=L1+L2S=L1+L2 步
快指针走了 F=L1+L2+nCF=L1+L2+nC 步(n为快指针在环内多绕的圈数)
由于快指针速度是慢指针的2倍:
F==2S代入:
L1+L2+nC=2(L1+L2)
nC=L1+L2
L1=nC−L2
代码流程
初始化快慢指针:
fast和slow指针都从头开始,快指针走两步,慢指针走一步。找环的相遇点:快慢指针相遇时,说明链表有环。
找环入口:一个指针从头开始,另一个从相遇点开始,两个指针同步走,首次相遇即为环的入口。
返回环入口:返回环的入口节点,如果无环则返回
null。
代码如下
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
// 快慢指针找相遇点
while (fast != null && fast.next != null) {
fast = fast.next.next; // 快指针走两步
slow = slow.next; // 慢指针走一步
// 发现环
if (fast == slow) {
// 一个指针从头开始,一个从相遇点开始
ListNode curr = head;
// 两者相遇点即为环入口
while (curr != slow) {
curr = curr.next;
slow = slow.next;
}
return curr; // 返回环入口
}
}
return null; // 无环
}
}
合并两个有序链表

思考方向
可以利用两个指针(一个指向
list1,另一个指向list2),按顺序比较两个链表的节点,依次将较小的节点插入到结果链表中。为了简化操作(如处理链表头部插入时的特殊情况),使用一个虚拟头节点来作为最终结果链表的起始点。
代码流程
创建虚拟头节点
dummy,简化操作,curr指向当前合并链表的最后节点。使用两个指针分别遍历
list1和list2,将较小的节点接到curr.next,并移动指针。当一个链表遍历完后,直接将另一个链表连接到
curr.next。最终返回
dummy.next,即合并后的链表头节点。
代码如下
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// 虚拟头节点,简化操作
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
// 双指针比较并合并
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
curr.next = list1;
list1 = list1.next;
} else {
curr.next = list2;
list2 = list2.next;
}
curr = curr.next;
}
// 连接剩余部分
curr.next = (list1 == null) ? list2 : list1;
return dummy.next;
}
}
两数相加

思考方向
题目要求将两个链表表示的数字相加,链表的每个节点存储一个数字的个位,低位在前,高位在后。需要模拟逐位相加的过程,考虑进位的情况。每位相加时,可能出现进位(和大于等于10),因此要处理进位的传递。最终的结果可能会多出一个进位位,因此链表的长度可能比原链表长。
代码流程
创建
dummy节点,curr指针指向当前结果链表的最后节点。同时遍历
l1和l2,每次取当前位的数字并计算和。计算当前位的和,更新进位
carry。创建新节点并连接到curr,移动curr指针。更新
l1和l2指针,直到所有节点和进位处理完。返回
dummy.next,即合并后的链表。
代码如下
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0); // 虚拟头节点
ListNode curr = dummy;
int carry = 0; // 进位值
// 遍历两个链表,直到都为空且无进位
while (l1 != null || l2 != null || carry != 0) {
// 获取当前位的值,链表为空则视为0
int num1 = (l1 == null ? 0 : l1.val);
int num2 = (l2 == null ? 0 : l2.val);
int sum = num1 + num2 + carry; // 计算和
ListNode node = new ListNode(sum % 10); // 当前位结果
carry = sum / 10; // 更新进位
curr.next = node; // 连接节点
curr = curr.next;
// 移动链表指针
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;
}
return dummy.next; // 返回结果链表
}
}
以上就是本篇文章对于力扣 hot 100链表章节的上班部分的全部的内容。希望能够帮助到看博客的各位。下半部分会加紧写出来。大家有什么疑问或者建议,可以在评论区进行留言。

浙公网安备 33010602011771号