24. 两两交换链表中的节点
做这类题需要更改next的时候要注意---当要更改next时候一定要将原本的next找一个临时变量存取
一般链表首先考虑创建一个虚拟头节点。
要理清需要几步,可以画图来想
下面这个更加直观
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next && cur->next->next) {
ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;
cur->next = cur->next->next; //步骤1
cur->next->next = tmp; //步骤2
cur->next->next->next = tmp1; //步骤三
cur = cur->next->next;
}
return dummyHead->next;//如果return head ,就是回到了原来第一个节点数经过交换后所在的位置就是第二个位置
}
};
第一次漏写了第三个步骤而且最后写成return head;
因为head为原来的第一个节点,经过交换后成为了第二个节点,如果return head就是从第二个节点开始了
19.删除链表的倒数第N个节点
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
这题一开始的想法是把从后删除改为从前面找,就类似于昨天的第一道链表题
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* cur = head;
int count = 0;
while (cur) {
count++;
cur = cur->next;
}
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur1 = dummyHead;
int index = count - n;
while (index--) {
cur1 = cur1->next;
}
ListNode* tmp = cur1->next;
cur1->next = cur1->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;
}
};
这里在做的时候犯了一个错误
ListNode* cur1 = dummyHead;写成了ListNode* cur1 = dummyHead->next;
导致后面出现空指针报错;
因为要删除某一个,或者对某一个节点进行操作就需要找到它前面的节点(因为前面的节点next指向需要删除的节点),所以要从虚拟头节点开始进行遍历
最后要将需要删除的节点赋给临时节点,然后删除临时节点,并将临时节点制成空指针
也可以使用双指针
思路如下
-
定义fast指针和slow指针,初始值为虚拟头结点,如图:
-
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
-
fast和slow同时移动,直到fast指向末尾,如题:
-
删除slow指向的下一个节点,如图:
-
- 关键在于fast指针要多走一步
- 双指针写法的代码
class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* cur = head; ListNode* dummyHead = new ListNode(0); dummyHead->next = head; ListNode*fast = dummyHead; ListNode*slow = dummyHead; while(n--){ fast= fast->next; } fast = fast->next; while(fast){ fast = fast->next; slow = slow->next; } slow->next = slow->next->next; return dummyHead->next; } };
面试题 02.07. 链表相交
这道题关键在于需要先将两个链表指针置为同一起点;
就需要先求出两个链表长度的差值;
下一步就是两个指针一起移动直到找到headA == headB
注意:
是求两个链表交点节点的指针,交点不是数值相等,而是指针相等
代码如下
class Solution {
public:
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
ListNode* cur1 = headA;
ListNode* cur2 = headB;
int account1 = 0, account2 = 0;
while (cur1) {
account1++;
cur1 = cur1->next;
}
while (cur2) {
account2++;
cur2 = cur2->next;
}
int c = account1 - account2;
if (c>0) {
while (c--) {
headA = headA->next;
}
}
else {
while (c!= 0) {
c++;
headB = headB->next;
}
}
while (headA && headB) {
if (headA == headB)
return headA;
else {
headA = headA->next;
headB = headB->next;
}
}
return NULL;
}
};
142.环形链表II
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
这是一道数学题了,可以使用快慢指针来模拟追击问题,解决这题关键在于:
1.找到快慢指针的相遇点
2.找到环的入口
假设这是各路段的距离
为什么一定会追上?
因为圈最少长为2,而快指针只比慢指针快1,每动一次快指针比慢指针多1,所以就一定会相遇。
那么相遇时: slow指针走过的节点数为: x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
公式可以理解v = s/t,这里t给省略了
我一开始疑惑的是fast走了n圈,难道slow就不会多走了几圈吗?
因为fast的速度是slow的两倍,并且他们没有在环口相遇,那么slow走半圈的时候,fast一定又走完了一圈,如果前半圈没有相遇,那么slow继续走半圈,fast又走一圈,那么他们一定会在slow接下来走的这半圈内相遇
接下来对公式化简
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = n (y + z) - y
,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
这个公式说明什么呢?
先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。
当 n为1的时候,公式就化解为 x = z
,
如果n>1的话,其实就只是相当于fast多走了几圈,直接剪掉(n-1)(y+z)就可以了
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
下面是代码
class Solution {
public:
ListNode* detectCycle(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
while (head != slow) {
head = head->next;
slow = slow->next;
}
return head;
}
}
return NULL;
}
};