代码随想录算法训练营第四天
24.两两交换链表中的节点
题目链接: 24. 两两交换链表中的节点
交换链表中的两个节点,需要2个临时变量备份节点,然后进行交换
/**
* 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 *cur =dummyhead;//0节点
while (cur->next != nullptr&&cur->next->next != nullptr){
ListNode * tmp1 = cur->next->next;//备份2节点
ListNode * tmp2 = cur->next->next->next; //备份3节点
tmp1->next = cur->next;//备份的2节点指向1节点
cur->next->next = tmp2;//1节点指向备份的3节点
cur->next = tmp1;//0节点指向备份的2节点
cur = cur->next->next;//控制节点后移2位
}
ListNode* result = dummyHead->next;
delete dummyHead;
return result;
}
};
19.删除链表的倒数第N个节点
题目链接: 19.删除链表的倒数第N个节点
创建一个双指针,让两个指针之间的距离相隔n,两个指针一起向后移动,当右指针移动到链表尾部时,左指针正好移动到要删除节点的前一个节点,然后删除左指针指向节点的后一个节点即可
/**
* 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* left= dummyhead;
ListNode* right = dummyhead;
for(int i = 0;i<n;i++){
right = right->next;
if(right == nullptr)return nullptr;//防止n>数组长度越界
}
while(right->next != nullptr){
left = left->next;
right = right->next;
}
ListNode * tmp = left->next;
left->next = left->next->next;
delete tmp;
ListNode* result = dummyhead->next;
delete dummyhead;
return result;
}
};
面试题 02.07. 链表相交
题目链接: 面试题 02.07. 链表相交
先遍历两个列表,计算出两个链表的长度,如果链表为空直接返回。计算链表长度差值gap,将长的链表一个指针从头向后移动gap个节点。这样两个链表的尾部就对齐了,最后两个指针同时后移如果两个指针相等则返回该指针
/**
* 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) {
if(headA==nullptr||headB==nullptr)return nullptr;//先判空,如果不判空,后面循环会访问空指针报错
int lenA = 0;
int lenB = 0;
ListNode* curA = headA;
ListNode* curB = headB;
while (curA != nullptr) {
curA = curA->next;
lenA++;
}
while (curB != nullptr) {
curB = curB->next;
lenB++;
}
curA = headA;//两个指针都回到初始位置
curB = headB;
if (lenA > lenB) {//选出长的一个后移gap个节点,然后尾端对齐
int gap = lenA - lenB;
for (int i = 0; i < gap; i++) {
curA = curA->next;
}
} else {
int gap = lenB - lenA;
for (int i = 0; i < gap; i++) {
curB = curB->next;
}
}
while(curA!=nullptr){//两个指针同时后移直到找到相等的指针
if(curA==curB){
return curA;
}
curA = curA->next;
curB = curB->next;
}
return nullptr;
}
};
142.环形链表II
题目链接:142.环形链表II
定义一个快指针和一个慢指针,快指针比慢指针每次多走1个节点,如果存在环,则快指针会在环内追上慢指针,使得二者相等。
- 设链表头到环起点的距离为 L,环起点到快慢指针相遇点的距离为 x,环的长度为 C。当快慢指针相遇时,慢指针走过的距离为 L+x,快指针走过的距离为 L+x+n*C(n 是快指针在环内多转的圈数)。
- 因为快指针的速度是慢指针的两倍,所以快指针走的距离是慢指针的两倍,即 2(L+x)=L+x+n*C。
- 从这个等式中,我们可以得出 L+x=nC 或 L=nC−x。
- 这意味着从链表头到环起点的距离 L 等于从相遇点继续走到环起点的距离(即 C−x,也是环的长度减去相遇点到环起点的距离),再加上整数倍的环长度 nC。
因此,当从链表头和相遇点同时出发,以相同的速度前进时,会在环的起点相遇。这就是为什么这种方法能够准确地找到环的起始节点。
/**
* 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 *index1 = head;
ListNode *index2 = fast;
while(index1!=index2){
index1 = index1 -> next;
index2 = index2 -> next;
}
return index1;
}
}
return nullptr;
}
};
总结
链表在内存中是离散存在的,靠指针相连。指针(赋值)相等,并不会复制指针指向的内容,当指针里的内容被修改时指向同一个地址的两个指针变量都会被修改。