链表相关题目笔记
24. 两两交换链表中的节点
题目:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
解题思路
- 使用虚拟头节点
dump
,这样即使链表为空或只有一个节点,也能方便处理。 - 维护三个指针:
pre
,cur
和dis
。cur
指向需要交换的第一个节点,dis
指向需要交换的第二个节点。- 交换这两个节点,并调整
pre.next
。
- 更新
pre
到cur
以继续处理后续节点。 - 时间复杂度为
O(n)
,空间复杂度为O(1)
。
代码
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dump = new ListNode();
dump.next = head;
ListNode pre = dump;
ListNode cur, dis;
while(pre.next != null && pre.next.next != null) {
cur = pre.next;
dis = cur.next;
// 执行交换
cur.next = dis.next;
dis.next = cur;
pre.next = dis;
// 更新指针
pre = cur;
}
return dump.next;
}
}
19. 删除链表的倒数第N个节点
题目:给你一个链表,删除链表的倒数第 n
个节点,并且返回链表的头结点。进阶:你能尝试使用一趟扫描实现吗?
解题思路
-
方法一:通过两次遍历实现
- 第一次遍历链表计算节点数量
size
。 - 第二次遍历找到倒数第
n
个节点的前一个节点,修改指针删除该节点。 - 时间复杂度
O(2n)
。
- 第一次遍历链表计算节点数量
-
方法二:双指针一次遍历实现
- 使用双指针
fast
和low
。让fast
先移动n + 1
步,使得fast
和low
之间保持n
个节点的间隔。 - 然后同步移动
fast
和low
直到fast
到达链表末尾。此时,low
指向待删除节点的前一个节点。 - 时间复杂度
O(n)
。
- 使用双指针
代码
方法一
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int size = 0;
ListNode dump = new ListNode();
dump.next = head;
ListNode cur = dump;
while(cur != null) {
size++;
cur = cur.next;
}
cur = dump;
for(int i = 0; i < size - n - 1; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
return dump.next;
}
}
方法二
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dump = new ListNode();
dump.next = head;
ListNode fast = dump;
ListNode low = dump;
for(int i = 0; i < n + 1; i++) {
fast = fast.next;
}
while(fast != null) {
low = low.next;
fast = fast.next;
}
low.next = low.next.next;
return dump.next;
}
}
160. 链表相交
题目:给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
解题思路
- 通过计算两个链表的长度差
size1
和size2
,让较长的链表先走|size1 - size2|
步,以便同步对齐。 - 然后同时遍历两个链表,找到相同的节点即为交点。
- 时间复杂度为
O(m + n)
,空间复杂度为O(1)
。
代码
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode dump1 = new ListNode();
ListNode dump2 = new ListNode();
dump1.next = headA;
dump2.next = headB;
ListNode cur1 = dump1;
ListNode cur2 = dump2;
int size1 = 0;
int size2 = 0;
while(cur1 != null) {
size1++;
cur1 = cur1.next;
}
while(cur2 != null) {
size2++;
cur2 = cur2.next;
}
cur1 = dump1;
cur2 = dump2;
if(size1 > size2) {
for(int dis = size1 - size2; dis > 0; dis--) {
cur1 = cur1.next;
}
} else {
for(int dis = size2 - size1; dis > 0; dis--) {
cur2 = cur2.next;
}
}
while(cur1.next != cur2.next) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1.next;
}
}
142. 环形链表 II
题目:给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null
。
解题思路
- 使用快慢指针
low
和fast
。 - 首先移动两个指针检测是否存在环,如果
low
和fast
相遇,则证明存在环。 - 将其中一个指针重置到头节点,两个指针每次都移动一步,再次相遇时即为入环的第一个节点。
- 时间复杂度
O(n)
,空间复杂度O(1)
。
代码
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode low = head;
ListNode fast = head;
while(fast != null && fast.next != null) {
low = low.next;
fast = fast.next.next;
if(low == fast) {
low = head;
while(low != fast) {
low = low.next;
fast = fast.next;
}
return low;
}
}
return null;
}
}