链表
24 两两交换链表中的节点
解题思路:
题解:
递归法
时间复杂度:O(n):操作 n 个节点
空间复杂度:O(n):递归栈深度为 n
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) return head;
ListNode node2 = head.next;
ListNode next = node2.next;
node2.next = head;
head.next = swapPairs(next);
return node2;
}
}
迭代法
时间复杂度:O(n):操作 n 个节点
空间复杂度:O(1)
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) return head;
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
while (cur.next != null && cur.next.next != null) {
ListNode node1 = cur.next;
ListNode node2 = cur.next.next;
ListNode next = node2.next; // 保存后面节点
cur.next = node2; // 开始交换
node2.next = node1;
node1.next = next;
cur = node1; // 更新 cur 下标
}
return dummyHead.next;
}
}
19 删除链表的倒数第N个节点
解题思路:
快慢指针,链表常用思路。让快指针先走 n 步或 n + 1 步(取决于你终止条件怎么写),然后快慢指针一起走。慢指针指向的就是倒数第 n 个节点。
题解:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode slow = dummyHead;
ListNode fast = dummyHead;
for (int i = 0; i < n; i++) {
fast = fast.next;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
}
02.07 链表相交
解题思路:
很经典的题目,自己第一次去想有点难。核心就是二者如果相交的话,它们的后半部分一定是一样的。将两条链表拉直,如果长度不一致,则交点至少是从较短链表的头节点开始。所以可以先将长链表节点遍历到较短链表头节点的长度位置。然后二者再一起向后遍历,如果二者相等则说明相交,直接返回。
题解:
时间复杂度:O(n + m):链表A + 链表B 的长度
空间复杂度:O(1)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
int lengthA = 1;
int lengthB = 1;
ListNode temp1 = headA;
ListNode temp2 = headB;
// 将两个节点都遍历到最后一个
while (temp1.next != null) {
temp1 = temp1.next;
lengthA++;
}
while (temp2.next != null) {
temp2 = temp2.next;
lengthB++;
}
// 如果两个节点到最后一个节点都不相等的话则二者不相交
if (temp1 != temp2) return null;
// 让 A 始终为较短的那个
if (lengthA > lengthB) {
// 将二者长度交换
int tempLen = lengthA;
lengthA = lengthB;
lengthB = tempLen;
// 将二者节点交换
ListNode tempNode = headA;
headA = headB;
headB = tempNode;
}
// 计算二者长度差
int gap = lengthB - lengthA;
// 双方从各自的倒数第n个开始向后遍历
while (gap-- > 0) {
headB = headB.next;
}
while (headA != headB) {
headA = headA.next;
headB = headB.next;
}
return headA;
}
}
142 环形链表II
解题思路:
这道题需要自己在草稿纸上模拟下,然后进行计算。
- 首先要判断是否有环,可以用快慢指针来解决。快指针每次移两个位置,慢指针每次移一个位置。二者如果能够相等则证明有环,或者快指针指向空了,也能证明无环。
- 其次就是要判断环的入口了。这里直接参考 Carl 的计算过程。核心就是快指针速度是慢指针的两倍,然后始终记得要求的是入口,所以 当 x = z 证明出来的时候就可以让两个指针一块移动了。
题解:
时间复杂度:O(n)
空间复杂度:O(1)
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
// 设置快慢指针,二者相对速度为1
fast = fast.next.next;
slow = slow.next;
// 二者相遇则证明存在环
if (fast == slow) {
// 通过数学计算,二者此时到交点的距离相等
while (slow != head) {
slow = slow.next;
head = head.next;
}
return head;
}
}
// 如果能遍历到null则证明无环
return null;
}
}
今日心得
链表这些题都是技巧性很强,也都是需要熟练掌握。多刷,默写就完了。