24. 两两交换链表中的节点
思路
- 判断出交换操作的最小操作单元,这里为两个节点的交换操作
- 判断结束操作的临界条件,这里依照链表的个数不同,给到不同的结束条件
- 判断操作过程中会断链的节点,用临时指针保存
解题方法
复杂度
-
时间复杂度:
O ( n ) O(n) O(n) -
空间复杂度:
O ( 1 ) O(1) O(1)
Code
Java
/**
* 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(-1, head);
// 需要操作两个节点,因此将指针指向待处理节点的前一个节点,保证能操作到
ListNode cur = dummy;
// 两个辅助指针,用于临时存放在操作过程中会断链的节点
ListNode temp1 = null;
ListNode temp2 = null;
// 如果链表个数为奇/偶数,末尾的判断条件不同
while (cur.next != null && cur.next.next != null) {
temp1 = cur.next;
temp2 = cur.next.next.next;
// 1
cur.next = cur.next.next;
// 2
cur.next.next = temp1;
// 3
temp1.next = temp2;
// 下一个操作单元的cur指针位置
cur = cur.next.next;
}
return dummy.next;
}
}
19.删除链表的倒数第N个节点
思路
- 设置虚拟头节点保证单链表所有节点的操作一致
- 使用快慢指针同向遍历,其中快指针初始位置指向虚拟头节点,既处于 -1 索引的位置
- 快指针先跑
n
个位置,然后快慢指针同时移动,直到快指针到末尾节点 - 此时慢指针指向的位置就是待删除元素的前一个位置
解题方法
同向双指针 & 快慢指针
复杂度
-
时间复杂度:
O ( n ) O(n) O(n) -
空间复杂度:
O ( 1 ) O(1) O(1)
Code
Java
/**
* 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(-1, head);
ListNode slowIndex = dummy;
ListNode fastIndex = dummy;
if (dummy.next == null) {
return null;
}
// 快指针先走 n 步
for (int i = 0; i < n; i++) {
fastIndex = fastIndex.next;
if (fastIndex == null) {
return dummy.next;
}
}
// 快慢指针同时走,快指针到达末端后,慢指针指向的就是待删除元素的前一个(初始指向虚拟节点,相当于指向 -1 索引)
while (fastIndex.next != null) {
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
// 删除(指向后一个)
slowIndex.next = slowIndex.next.next;
return dummy.next;
}
}
面试题 02.07. 链表相交
思路
1.如果有相交的部分,则从末端到相交点一定是共用的,也即长度相同,基于这点,可以使用双指针法
2. 找到两个链表可能的最长相交位置,也即短的链表的头节点
3. 从这个节点开始,逐个节点对比
解题方法
双指针法,同向对比
复杂度
-
时间复杂度:
O ( n ) O(n) O(n) -
空间复杂度:
O ( 1 ) O(1) O(1)
Code
Java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 遍历获取两个链表的长度
ListNode curA = headA;
ListNode curB = headB;
int sizeA = 0;
int sizeB = 0;
while (curA != null) {
curA = curA.next;
sizeA++;
}
while (curB != null) {
curB = curB.next;
sizeB++;
}
curA = headA;
curB = headB;
// 判断长度,让长链为A变量
if (sizeB > sizeA) {
// swap(curA, curB);
ListNode temp = curA;
curA = curB;
curB = temp;
// swap(sizeA, sizeB);
int tempSize = sizeA;
sizeA = sizeB;
sizeB = tempSize;
}
// 长链表的指针先走,直到对齐长度
for (int i = 0; i < sizeA - sizeB; i++) {
curA = curA.next;
}
// 双指针同步遍历,直到相交点
while (curA != null) {
if (curA == curB) {
return curA;
} else {
curA = curA.next;
curB = curB.next;
}
}
return null;
}
}
142.环形链表II
思路
这题有两问
判断是否有环
- 使用快慢指针,慢指针走一步,快指针走两步。
- 如果无环,则快指针或快指针的下一个一定会遇到null
- 如果有环,则快指针先进环,快指针会相对慢指针以一格的速度追赶,一定会在慢指针走完一环之前追到 --> 可证明
判断环的起点
按照图示,有个等式:x = (n - 1)(y + z) + z
,其中
n
为相遇时,快指针跑的完整圈数,n 一定大于 1(y + z)
等于一整圈
综上,如果给两个指针,一个在头节点,一个在相遇点,同时向前走,则相遇点一定是环形的入口节点
解题方法
复杂度
-
时间复杂度:
O ( n ) O(n) O(n) -
空间复杂度:
O ( 1 ) O(1) O(1)
Code
Java
/**
* 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) {
ListNode fast = head;
ListNode slow = head;
// 判断是否有环
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
// 快慢指针相遇
if (fast == slow) {
// 新起动两个指针,一个在相遇点,一个在头节点,同时向前走,相遇时就是环的起点
ListNode index1 = head;
ListNode index2 = fast;
while(index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
// 快指针遇到了null, 无环
return null;
}
}