相交链表
反转链表
回文链表
环形链表
环形链表 II
160. 相交链表



public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
解题思路1:双指针
双指针法是最优解,时间复杂度 O(n+m),空间复杂度 O(1):
-
初始化两个指针
pA和pB,分别指向链表headA和headB的头节点。 -
同时遍历两个链表,
pA走到headA末尾时,重新指向headB;pB走到headB末尾时,重新指向headA。 -
当
pA和pB相遇时,该节点即为相交的起始节点;若遍历到末尾仍未相遇,则两链表不相交,返回null。
原理 :两个指针走过的总路程相等(lenA + lenB),若存在相交节点,必然会在相交点相遇
代码解释
-
边界处理 :若任一链表为空,直接返回
null。 -
指针遍历 :
pA和pB交替遍历两个链表,保证最终路程相等。 -
相遇判断 :当
pA == pB时,要么是相交节点,要么同时到达末尾(null),直接返回即可。public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { int lenA = 0, lenB = 0; ListNode pA = headA, pB = headB; // 计算长度 while (pA != null) { lenA++; pA = pA.next; } while (pB != null) { lenB++; pB = pB.next; } // 长链表先走 pA = headA; pB = headB; if (lenA > lenB) { for (int i = 0; i < lenA - lenB; i++) pA = pA.next; } else { for (int i = 0; i < lenB - lenA; i++) pB = pB.next; } // 同时遍历找相遇点 while (pA != pB) { pA = pA.next; pB = pB.next; } return pA; } }解题思路2:长度差法
先计算两个链表的长度,让长链表的指针先走长度差的步数,再同时遍历,相遇点即为相交节点
206. 反转链表

/**
- 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 reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next; // 保存下一个节点
curr.next = prev; // 翻转当前节点指向
prev = curr; // prev 前进
curr = next; // curr 前进
}
return prev;
}
}
解题思路1:迭代法
核心思路 :用三个指针逐个翻转节点的指向,从前往后遍历链表,将当前节点的 next 指向前一个节点。
-
初始化
prev = null(前一个节点)、curr = head(当前节点)、next = null(临时保存下一个节点) -
遍历链表:
-
保存
curr.next到next,避免断链 -
将
curr.next指向prev,完成当前节点的翻转 -
prev移动到curr,curr移动到next
-
-
遍历结束后,
prev就是新链表的头节点class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
解题思路2:递归法
核心思路:递归到链表末尾,从后往前逐层翻转节点指向。
-
递归终止条件:
head == null或head.next == null,直接返回head -
递归调用
reverseList(head.next)得到子链表的新头节点 -
将
head.next.next = head,让子链表的末尾节点指向当前节点 -
将
head.next = null,断开原指向,避免成环
234. 回文链表

/**
* 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 boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) return true;a
// 1. 快慢指针找中点
ListNode slow = head, fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 2. 反转后半链表
ListNode prev = null, curr = slow.next;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
// 3. 双指针比较
ListNode left = head, right = prev;
while (right != null) {
if (left.val != right.val) return false;
left = left.next;
right = right.next;
}
return true;
}
}
解题思路1:双指针 + 反转后半链表
-
用快慢指针找到链表中点(快指针走 2 步,慢指针走 1 步,快指针到末尾时慢指针指向中点)。
-
反转链表的后半部分。
-
用双指针分别从链表头和反转后的后半部分头开始遍历,逐一比较节点值是否相等。
-
若所有节点值都相等,则为回文链表;否则不是。
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> list = new ArrayList<>();
ListNode curr = head;
while (curr != null) {
list.add(curr.val);
curr = curr.next;
}
int left = 0, right = list.size() - 1;
while (left < right) {
if (!list.get(left).equals(list.get(right))) return false;
left++;
right--;
}
return true;
}
}
解题思路2:转数组 + 双指针
遍历链表,将所有节点值存入数组。
用双指针分别从数组首尾向中间遍历,逐一比较元素是否相等。
141. 环形链表

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
}
解题思路1:快慢指针法(Floyd 判圈算法)
-
初始化两个指针
slow(慢指针,每次走 1 步)和fast(快指针,每次走 2 步)。 -
若链表无环:快指针会先到达链表末尾(
fast == null或fast.next == null),直接返回false。 -
若链表有环:快指针最终会和慢指针在环内相遇,此时返回
true。
原理:快指针相对慢指针的速度为 1 步 / 次,若存在环,两者必然会在环中相遇。
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> visited = new HashSet<>();
while (head != null) {
if (visited.contains(head)) return true;
visited.add(head);
head = head.next;
}
return false;
}
}
解题思路2: 哈希表 法(超低效率)
-
遍历链表,用哈希集合记录已访问的节点。
-
若当前节点已在集合中,说明存在环;若遍历到末尾仍未重复,则无环。
142. 环形链表 II

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) return null;
ListNode slow = head, fast = head;
// 1. 快慢指针找相遇点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) break; // 找到相遇点
}
// 无环情况
if (fast == null || fast.next == null) return null;
// 2. slow 移回头部,同速遍历找入口
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
解题思路1:快慢指针法(Floyd 判圈算法)
-
检测环并找到相遇点:
-
用慢指针
slow(每次走 1 步)和快指针fast(每次走 2 步)遍历链表。 -
若
fast或fast.next为null,说明无环,返回null。 -
若
slow与fast相遇,说明存在环。
-
-
找到环的入口:
-
相遇后,将
slow移回链表头节点,fast留在相遇点。 -
让
slow和fast以相同速度 (每次 1 步)继续遍历,两者再次相遇的节点即为环的入口。
-
数学原理 :设链表头到环入口的距离为 a,环入口到相遇点的距离为 b,环的长度为 c。
-
相遇时,
slow走过的路程:a+b -
fast走过的路程:a+b+k⋅c(k 为绕环的圈数,k≥1) -
因为
fast速度是slow的 2 倍,所以:2(a+b)=a+b+k⋅c,化简得 a=k⋅c−b。 -
这意味着:从链表头到环入口的距离 = 从相遇点绕环 k圈后再走到环入口的距离 。因此,让
slow从头部出发,fast从相遇点出发,同速前进,必然在环入口相遇。public ListNode detectCycle(ListNode head) {
Set<ListNode> visited = new HashSet<>();
while (head != null) {
if (visited.contains(head)) return head;
visited.add(head);
head = head.next;
}
return null;
}
解题思路2: 哈希表 法(超低效率)
遍历链表,用哈希集合记录已访问的节点,第一个重复出现的节点即为环的入口。
核心题型与最优解法汇总
| 题目 | 核心解法 | 时间复杂度 | 空间复杂度 | 核心思想 |
|---|---|---|---|---|
| 160. 相交链表 | 双指针法(路程对等) | O(n+m) | O(1) | 两指针分别遍历 A+B 和 B+A,路程相等则必在相交点相遇 |
| 206. 反转链表 | 迭代法(双指针) | O(n) | O(1) | 逐个翻转节点指向,用 prev/curr/next 三个指针完成方向反转 |
| 234. 回文链表 | 快慢指针 + 反转后半链表 | O(n) | O(1) | 找中点→反转后半段→双指针对比前后段,实现原地判断 |
| 141. 环形链表 | 快慢指针(Floyd 判圈) | O(n) | O(1) | 快指针速度 2 倍于慢指针,有环则必相遇 |
| 142. 环形链表 II | 快慢指针 + 入口定位 | O(n) | O(1) | 相遇后慢指针回起点,双指针同速遍历,相遇点即为环入口 |
