代码随想录day6-链表(2)
1、LeetCode 19 删除链表的倒数第 N 个结点
题目分析:
从分析的角度确实不够严谨,这个题就是典型的双指针的题目。要求删除倒数的第N个结点,我们设置一个先后指针,先指针从正数第N+1个结点出发,后指针从dummyHead出发,这样,当先指针到达nullptr的时候,后指针正好可以到达需要删除的前一个结点地方,二者中间有N个结点,也就达到了删除倒数第N个结点的目的。注意这里好好理解这个N+1以及虚拟结点。
题目解答:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0, head);
ListNode* behind = dummyHead; // 后结点
ListNode* front = head; // 前结点
while(n-- && front) {
front = front->next;
}
// 此时front指向的就是第n+1个结点
while (front) {
front = front->next;
behind = behind->next;
}
// 此时behind就是需要删除的结点的前一个结点
ListNode* temp = behind->next;
behind->next = behind->next->next;
ListNode* ans = dummyHead->next;
delete temp;
delete dummyHead;
return ans;
}
};
2、LeetCode 160 链表相交
题目分析:
本题最简单的思路就是分别遍历两个链表,获得两个链表的长度,然后将两个链表按照尾巴对齐,然后从短的头部进行遍历,就能得到链表相交的起始结点。
题目解答:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0;
int lenB = 0;
// 分别获取链表的长度
while (curA) {
lenA++;
curA = curA->next;
}
while (curB) {
lenB++;
curB = curB->next;
}
// 将二者按照尾巴对齐,起始就是长的链表的第一个指针移动一下
curA = headA;
curB = headB; // 让指针重新指到头结点
if (lenA < lenB) {
swap(curA, curB);
swap(lenA, lenB); // 这个操作就相当于是,长的链表就是curA,短的链表就是curB
}
for (int i = 0 ; i < lenA - lenB; i++) {
curA = curA->next; // 长的第一个结点进行移动,保证跟短的一样长
}
// 二者同时遍历
for (int i = 0; i < lenB; i++) {
if (curA == curB) {
return curB;
}
else {
curA = curA->next; // 二者同步增加
curB = curB->next;
}
}
return nullptr;
}
};
注意这里使用swap函数,将链表以及链表的长度都进行交换,使得长的链表始终为curA所指向的链表。
3、LeetCode 142 环形链表Ⅱ
题目分析:
这个题也是链表中双指针的典型应用,一开始根本不太会,直接看的卡尔的解答,思想非常重要。
主要有一个数学上的推导,可以让思路变得很简单。
首先我们设置两个指针,一个快指针,一个慢指针。快指针每次移动两个,慢指针每次移动一个,这样能保证二者一旦同时进入环中,二者的相对速度是1,快指针总能追上慢指针。
根据上述的图,可以列出等式:
2*(x+y) = x+y+n(y+z),其中n是快指针在环内转的圈数,整理一下得到:
x = (n-1)(y+z)+z,这个式子表明:一旦存在环,那么x也就是环入口点一定等于相遇点加n-1个环那么长。知道了这一点之后,就可以开始写代码了。
题目解答:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
// fast指针每次移动两个,slow指针每次移动一个
while (fast && fast->next) {
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;
}
}
return nullptr;
}
};
如果这个题目追问一下环的长度呢?
当fast==slow代表有环,然后fast和slow继续指向next,然后知道下一次相遇,slow走过的正好就是一圈,因为fast相当于走了两圈,正好追上。
**总结:**这两天关于链表的题目稍微做了一些,有一点儿初步的感觉,主要两个点就是双指针以及虚拟头结点,等一刷完成后,再做做这些题,顺便扩展一下其他的题目。