LeetCode 24.两两交换链表中的节点
题目链接:24.两两交换链表中的节点
踩坑:一开始觉得用不了虚拟头节点,有一次没考虑到特殊情况下空指针的问题
思路:核心是要意识到两个节点交换后,如由1->3->4变为1->4->3时,1要指向4,即在操作需要反转的两个节点的时候,前一个节点的地址也要留着。因此,可以使用双指针或三指针。在纸上画一下过程写代码就会简单很多。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0, head);
ListNode* pre = dummyhead;
ListNode* p = head;
while(p != nullptr && p->next != nullptr)
{
ListNode* t = p->next;
p->next = t->next;
t->next = p;
pre->next = t;
pre = p;
p = p->next;
}
return dummyhead->next;
}
};
LeetCode 19.删除链表的倒数第N个节点
题目链接:19.删除链表的倒数第N个节点
踩坑:没什么大坑,只是需要注意循环的次数,需要将pre指针控制在倒数第N+1个节点。
思路:可以先遍历一趟确定size,再遍历一边确定倒数第N个。也可以让一个指针先出发走n+1个,再两个指针同时走。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0, head);
ListNode* pre = dummyhead;
ListNode* p = head;
int size = 0;
while(p != nullptr)
{
size++;
p = p->next;
}
int step = size - n;
while(step--)
{
pre = pre->next;
}
ListNode* t = pre->next;
pre->next = t->next;
if(t != nullptr) delete t;
return dummyhead->next;
}
};
面试题02.07.链表相交
题目链接:面试题02.07.链表相交
踩坑:简单的才是最有效的!别一直想一些很巧妙的方法,多遍历几遍不丢人。
思路:就是分别遍历A,B拿到size,然后对齐长度一起遍历。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* pa = headA;
ListNode* pb = headB;
int sizea = 0, sizeb = 0;
while(pa != nullptr)
{
sizea++;
pa = pa->next;
}
while(pb != nullptr)
{
sizeb++;
pb = pb->next;
}
int gap = sizea - sizeb;
if(gap > 0)
{
pa = headA;
pb = headB;
// 对齐起点
while(gap--) pa = pa->next;
while(pa != nullptr && pb != nullptr)
{
if(pa == pb) return pa;
pa = pa->next;
pb = pb->next;
}
return nullptr;
}
else
{
pa = headA;
pb = headB;
gap = -gap;
// 对齐起点
while(gap--) pb = pb->next;
while(pa != nullptr && pb != nullptr)
{
if(pa == pb) return pa;
pa = pa->next;
pb = pb->next;
}
return nullptr;
}
}
};
LeetCode 142.环形链表||
题目链接:142.环形链表||
踩坑:这道题技巧性太强了,由于之前没做过有关环的题目,直接看题解了(哭哭)
思路:这道题的核心就是要明确什么时候循环停止(这也是我自己思考的时候卡壳的地方),但是是真的想不到要用到追及问题,应该是解决这类问题的范式了(记忆加理解就好),而且快指针一次走两个也是很有说法的,一是不会漏过慢指针,二是确保了在慢指针入环后的第一圈就能追上慢指针。因为就算不确定慢指针入环时快指针的位置,最差最差也是落后不到一圈,而相同的时间慢指针只能走一圈,快指针却能走两圈,所以一定会追上。另外,追上后在相遇的位置开始一次一步的指针与在起点同时开始一次一步的指针必定在入环位置相遇的推导也是巧妙。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)// 快慢指针相遇
{
ListNode* meet = fast;
ListNode* start = head;
// 若相遇必定是入环位置
while(start != meet)
{
start = start->next;
meet = meet->next;
}
return meet;
}
}
return nullptr;
}
};