您现在的位置是:首页 >技术交流 >代码随想录算法训练营第四天|24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II网站首页技术交流
代码随想录算法训练营第四天|24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II
两两交换链表中的节点
题目链接:力扣
解题思路:虚拟头节点,然后进行模拟即可。
我拿到这道题的时候,其实交换的思路是有的,但是首先没有设虚拟节点,这使得我的解答很乱,有很多if条件判断。其次卡哥的答案思路就很清晰,我的思路目标有点大,想使得一次交换完后1能直接指向4,这样也会使代码变得复杂。
这题就贴上卡哥的代码,供自己学习
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
return dummyHead->next;
}
};
- 时间复杂度:O(n) 空间复杂度:O(1)
删除链表的倒数第N个节点
题目链接:力扣
解题思路:
1、最开始想到的是暴力的方式:先算得总链表的长度,然后算出倒数第N个节点是正数第几个节点,再用删除链表节点的常规
操作。但是这样要用到两次遍历。
下面给出我的暴力解法:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *VriNode = new ListNode(-1); //虚拟头节点
VriNode->next = head;
ListNode* Result = VriNode;
int size = ListSize(VriNode);
if(n>size)
return nullptr;
int Index = size-n-1; //寻找的是删除节点的前一个节点
while(Index--)
VriNode = VriNode->next;
VriNode->next = VriNode->next->next;
return Result->next;
}
int ListSize(ListNode *head)
{
int size = 0;
while(head!=nullptr){
head = head->next;
size++;
}
return size;
}
};
快慢指针法:
设虚拟节点,快指针和慢指针同时指向虚拟节点,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
但是值得注意的是,因为要删除某个节点的话,就要使指针指向该节点的前一个节点,所以要想删除某一节点,就要是slow指向该节点的前一节点,所以fast要先走n+1步。
下面贴上卡哥快慢指针的答案:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
// ListNode *tmp = slow->next; C++释放内存的逻辑
// slow->next = tmp->next;
// delete nth;
return dummyHead->next;
}
};
面试题 02.07. 链表相交
题目链接:力扣
解题思路:
1、两次遍历,第一次用vector装链表A的节点,第二次遍历链表B中有没有A的节点,有则返回。
2、求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,此时就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
环形链表II
题目链接:力扣
思路:环形链表的题目都是有套路的,这题在剑指offer里做过,其中给的思路是:
首先确定是否有环:快慢指针,慢指针走一步,快指针走两步,若能相遇则有环;
其次确定环的大小,就在上一步相遇的地方一直找节点的next,直到重新回到开始的节点,记录环数为n;
最后快慢指针重新回到起点,快指针先走n步,再快慢指针同时走一步,直到相遇,相遇的点就是环形链表的入口节点。
附上我的代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* low = head;
ListNode* quick = head;
int CircleSize = 0;
while(1)
{
if(low)
{low = low->next; cout<<"low go"<<endl;} //慢指针走一步
else
break;
if(quick && quick->next)
{quick = quick->next->next; cout<<"quick go"<<endl;} //快指针走两步
else
break;
if(low == quick) //此时说明有环,计算出环的大小
{
ListNode *start = low;
CircleSize = 1;
while((low =low->next) != start)
{
CircleSize++;
}
cout<<CircleSize<<endl;
break;
}
}
if(CircleSize > 0) //有环时候的代码
{
low = head;
quick = head;
while(CircleSize--)
quick= quick->next; //链表中环有n个节点,则快指针多走一n步
while(low != quick) //之后以相同的频率向前走
{
low = low->next;
quick = quick->next;
}
return low;
}
return nullptr;
}
};
卡哥给出另一种思路:
确定有环后,慢指针回到起点,快指针还在之前相遇的环上,接着快慢指针同时走一步,直到快慢指针重合就是环形链表的入口节点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};