文章目录
前置知识
参考前文:
24. 两两交换链表中的节点
题目描述
LeetCode链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/
解题思路
没有太多注意,按照题目所说的进行交换即可
可能对于pre、cur、next的使用有点技巧
代码
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL)
return head;
ListNode* dummyHead = new ListNode(0, head);
ListNode* cur=head;
ListNode* prev=dummyHead;
while(1){
ListNode* next = cur->next;
if(cur==NULL || cur->next==NULL)
break;
prev->next = next;
cur->next = next->next;
next->next = cur;
prev = cur;
cur = cur->next;
if(cur==NULL || cur->next==NULL)
break;
}
return dummyHead->next;
}
};
19.删除链表的倒数第N个节点
题目描述
LeetCode链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
简单快慢指针
思路: 快慢指针, 快指针先遍历一遍得到链表总体长度length, 慢指针在得知链表长度后再出发
就删除length-n的节点即可
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL)
return head;
ListNode* dummyHead = new ListNode(0, head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
int length=0;
while(fast->next != NULL){
fast = fast->next;
length++;
}
length = length - n;
for(int i=0; i<length; ++i){
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
delete tmp;
return dummyHead->next;
}
};
进阶快慢指针
思路: 依然是快慢指针, 但是快指针先走, 走n步以后慢指针出发
快指针到达结尾的时候, 慢指针正好抵达目标
主打一个简洁
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL)
return head;
ListNode* dummyHead = new ListNode(0, head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
for(int i=0; i<n; ++i){
fast = fast->next;
}
while(fast->next != NULL){
fast = fast->next;
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
delete tmp;
return dummyHead->next;
}
};
面试题 02.07. 链表相交
题目描述
LeetCode链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
哈希表法
思路: 维护一个unordered_set, 遍历A的时候依次入, 然后遍历B, 有节点已存在那就返回这个节点
如果一直遍历完B都没有, 就返回NULL
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> set;
while(headA!=NULL){
set.insert(headA);
headA = headA->next;
}
while(headB!=NULL){
if(set.count(headB))
return headB;
headB = headB->next;
}
return NULL;
}
};
双指针对齐法
用哈希表不是不行, 但是空间复杂度过高, 参考题解, 使用双指针法
观察发现: A B的后半段是一样的, 那么只要让二者向右对齐(两个指针距离各自的尾节点距离一致)
然后再同时往后移动, 如果二者有相交, 就可以相遇
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA=headA;
ListNode* curB=headB;
int lengthA=0, lengthB=0;
while(curA != NULL){
curA = curA->next;
lengthA++;
}
while(curB != NULL){
curB = curB->next;
lengthB++;
}
// 让curA指向更长的
if(lengthA > lengthB){
curA = headA;
curB = headB;
}else{
curA = headB;
curB = headA;
}
int length = abs(lengthA-lengthB);
for(int i=0; i<length; ++i){
curA = curA->next;
}
while(curA != NULL){
if(curA == curB)
return curA;
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
142.环形链表II
题目描述
截图
LeetCode链接:xxx(记得加点击跳转链接)
unordered_set法
建立一个unordered_set, 遍历链表的过程中依次insert, 发现重复的就返回
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> set;
while(head!=NULL){
if(set.count(head))
return head;
set.insert(head);
head = head->next;
}
return NULL;
}
};
快慢指针法
如何用快慢指针判断有无环
只要快指针一次走两步, 慢指针一次走一步, 只要有环, 就一定会在某个点相遇;
所以可以用快慢指针判断有无环.
如何用快慢指针找到环入口
如上图所示:
快指针走过的距离是: fast = x+n(z+y)+y
慢指针走过的距离是: slow= x+y
又由快慢指针的速度: fast = 2*slow
有: x+n(z+y)+y = 2*(x+y)
化简: n*z + n*y = x + y
即: x = z + (n-1)*(x+y)
所以如果在快慢指针相遇的时候, 有一个新指针(速度和慢指针一样)从头结点出发, 那么当新指针和慢指针相遇的时候, 二者就指向入口节点.
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* ans=head;
while(ans!=fast){
ans = ans->next;
fast = fast->next;
}
return ans;
}
}
return NULL;
}
};
总结
发现没有? 就是掌握了链表的底层数据结构, 以及基础操作, 这些"上层"的题目真就只需要一些trick.
最容易出现bug的地方也不是思路不对, 而是基本的操作没办法下意识地正确实现.
基础不牢地动山摇啊