24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点. 不能只是交换节点的值,必须交换物理节点.
文章及视频讲解:代码随想录
思路:
我们在交换两个节点的时候,当前指针应该指向哪里呢?
我们可以联想到:在删除链表中的节点时,我们必须要知道被删除节点的前一个节点pre,才能完成节点的删除操作。那么同理,当我们要交换两个节点的时候,也要找到这两个节点的前一个节点,这是这道题的关键所在。
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead=new ListNode;
dummyhead->next=head; //定义虚拟头节点并指向头节点
ListNode* cur=dummyhead; //定义cur指针
while(cur->next!=NULL&&cur->next->next!=NULL){
//注意这里循环的结束条件!
//链表节点个数无非是偶数/奇数.
//偶数时的结束条件是:cur->next==NULL
//奇数时的结束条件是:cur->next->next==NULL(奇数情况就是比偶数多了一个节点)
/*
dummyhead->A->B->C->D....
^
|
cur
*/
ListNode* temp=cur->next; //保存A节点
ListNode* temp1=cur->next->next->next; //保存C节点
cur->next=cur->next->next; //让cur指向B节点
cur->next->next=temp; //让B节点指向A节点
temp->next=temp1; //让A节点指向C节点
cur=cur->next->next; //更新cur节点
}
return dummyhead->next; //返回头节点
}
19.删除链表的倒数第N个节点
文章以及视频讲解:代码随想录
思路:这道题我们可以用双指针中的快慢指针的思想来解决,这里我们仍然使用虚拟头节点,方便统一操作;
我们先定义两个指针,一个慢指针slow,一个快指针fast,开始时先让两个指针同时指向dummyhead,然后让fast指针先走n步,然后快慢指针同时移动,当fast指针移动到链表的最后一个节点时,slow指向的节点就是我们要删除的节点.
但是要想删除该节点,必须找到该节点的前一个节点,所以我们在刚开始的时候让fast先移动n+1步,那么最后slow指向的节点就是要删除的节点的前一个节点.
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead=new ListNode; //定义虚拟头节点
dummyhead->next=head;
ListNode* slow=dummyhead,*fast=dummyhead; //快慢指针指向dummyhead
int len=0; //计数器
while(len<n){ //将fast移动n+1个节点
fast=fast->next;
len++;
}
while(fast->next!=NULL){ //当fast指向链表最后一个节点时结束循环
fast=fast->next;
slow=slow->next; //同时移动快慢指针
}
//删除要删除的节点
ListNode* temp=slow->next;
slow->next=temp->next;
delete temp;
return dummyhead->next;
}
面试题02.07.链表相交
文章讲解:代码随想录
思路: 可以先分别求出两个链表的长度lenA,lenB.然后将长度较长的链表的cur指针移动到lenA-lenB位置,然后同时开始同时遍历A链表和B链表,并且比较指针是否相等,相等则返回指针,否则返回NULL.
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = 0, lenB = 0;
ListNode* curA = headA, *curB = headB;
// 计算链表 A 的长度
while (curA!= NULL) {
lenA++;
curA = curA->next;
}
// 计算链表 B 的长度
while (curB!= NULL) {
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让较长的链表先移动,使得两个链表剩余长度相同
while (lenA > lenB) {
curA = curA->next;
lenA--;
}
while (lenB > lenA) {
curB = curB->next;
lenB--;
}
// 同时遍历两个链表,寻找相交节点
while (curA!= NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
142.环形链表II
文章讲解:代码随想录
思路:
主要思想-- 快慢指针
判断环的存在: 我们定义一个快指针和一个慢指针,快指针每次走两个节点,慢指针每次走一个节点,如果该链表有环的话,那么快慢指针一定会在环里面相遇,因为快指针相对于慢指针是以每次移动一个节点的速度去慢慢靠近慢指针,所以快指针不会直接跳过慢指针.
寻找环的入口:
我们分析可以得出:
快指针路程=X+n(Z+Y)+Y
慢指针路程=X+Y(在这里快指针和慢指针初次相遇只会是在慢指针进入环的第一圈,可以数学证明,此处证明略)
则:2(X+Y)=X+n(Z+Y)==>X=(n-1)(Z+Y)+Z
我们可以看出,X和相遇点之间相差n圈再加上Z,那么我们在找出相遇点之后,可以设两个指针index1,index2,分别指向head和相遇点,让它们以相同的速度移动,那么两个指针相遇的位置就是环的入口位置.
ListNode *detectCycle(ListNode *head) {
ListNode* fast=head,*slow =head; //定义快慢指针
while(fast!=NULL&&fast->next!=NULL) /*这个条件保证了在移动 fast 指针时,
不会对空指针进行 fast->next->next 的操作,
因为 fast->next->next
要求 fast->next 必须不为 NULL。
*/
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow){ //快指针和慢指针初次相遇的位置
ListNode* index1=fast; //定义index1指向快慢指针初次相遇的位置
ListNode* index2=head; //定义index2指向head
while(index1!=index2){ //index1和index2以相同的速度向环的入口移动
index1=index1->next;
index2=index2->next;
}
return index1; //返回环的入口
}
}
return NULL; //无环
}