今日任务
24. 两两交换链表中的节点
方法一:正常遍历
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (!head || !head->next) return head;
ListNode *dummyHead = new ListNode(0, head->next), *pre = head, *rear = head->next, *tmp;
while (rear->next && rear->next->next) {
tmp = rear->next;
rear->next = pre;
pre->next = tmp->next;
pre = tmp;
rear = pre->next;
}
// if (!rear->next) {
// rear->next = pre;
// pre->next = nullptr;
// } else {
// tmp = rear->next;
// rear->next = pre;
// pre->next = tmp;
// }
// 最后边界两种情况可以合并
tmp = rear->next;
rear ->next = pre;
pre->next = tmp;
return dummyHead->next;
}
};
方法二:递归
class Solution {
public:
// 递归写法
ListNode* swapPairs(ListNode* head) {
if (!head || !head->next) return head;
ListNode *rear = head->next->next, *tmp = head->next;
head->next->next = head;
head->next = swapPairs(rear);
return tmp;
}
};
19. 删除链表的倒数第 N 个结点
方法一:正常遍历
先正常遍历得到链表大小,再计算删除哪个点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int len = 0;
ListNode *p = head;
while (p) {
len++;
p = p->next;
}
if (n == len) return head->next;
p = head;
for (int i = 0; i < len - n - 1; ++i) p = p->next;
p->next = p->next->next;
return head;
}
};
面试题 02.07. 链表相交
方法一:双指针
t i m e : O ( n ) time: O(n) time:O(n)
s p a c e : O ( 1 ) space: O(1) space:O(1)
核心思想:
pA先走listA,碰到null之后再换listB走(pB则镜像pA的操作)
![image-20230813210732644](https://feobay.oss-cn-shanghai.aliyuncs.com/img/image-20230813210732644.png)
此处记listA、listB中无交集部分长度分别为a、b,有交集部分长度为c
- 若两链表有交点,那么由于pA与pB都是一次一个节点,那么它们必然会同时走过距离
a+c+b
,此时两个指针正好都在交点处,直接返回交点即可 - 若两链表无交点,那么pA与pB肯定同时走完
a+b
的距离,所以也会同时为null
综上所述:pA与pB会在相等时结束循环,结束后pA=pB=(交点 或 null)
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pA = headA, *pB = headB;
while (pA || pB) { // pA与pB不同时为空即可
if (pA == pB) return pA;
pA = pA ? pA->next : headB; // 如果pA为空,pA指向headB
pB = pB ? pB->next : headA;
}
return nullptr;
}
};
简化的写法
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pA = headA, *pB = headB;
while (pA != pB) {
pA = pA ? pA->next : headB;
pB = pB ? pB->next : headA;
}
return pA;
}
};
方法二:哈希表
t i m e : O ( n ) time: O(n) time:O(n)
s p a c e : O ( n ) space: O(n) space:O(n)
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode *> existSet;
ListNode *p = headA;
while (p) {
existSet.insert(p);
p = p->next;
}
p = headB;
while (p) {
if (existSet.count(p)) return p;
p = p->next;
}
return nullptr;
}
};
142. 环形链表 II
方法一:哈希表
t i m e : O ( n ) time: O(n) time:O(n)
s p a c e : O ( n ) space: O(n) space:O(n)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> nodesSet;
ListNode *p = head;
while (p) {
if (nodesSet.count(p)) return p;
nodesSet.insert(p);
p = p->next;
}
return nullptr;
}
};
方法二:Floyd判环(快慢指针)
t i m e : O ( n ) time: O(n) time:O(n)
s p a c e : O ( 1 ) space: O(1) space:O(1)
![image-20230813213153361](https://feobay.oss-cn-shanghai.aliyuncs.com/img/image-20230813213153361.png)
上面的说明结论错误,但是关键在于证明 S a = k b S_a = kb Sa=kb
第一次相遇时,pA走了 S a S_a Sa,只要再走 a a a步即可到达圈的入口,此时让fast回到链表头走即可
关于证明可以见 环形链表 II(双指针法,清晰图解)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (!head) return nullptr;
// 构建第一轮相遇
ListNode *slow = head, *fast = head;
while (1) {
if (!fast->next || !fast->next->next) return nullptr;
slow = slow->next, fast = fast->next->next;
if (slow == fast) break;
}
fast = head;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};