第二章链表part02
1.LeetCode 两两交换链表中的节点
1.1题目链接:24.两两交换链表中的节点
1.2思路:两两节点进行交换,首先会想到奇数节点和偶数节点两种情况,在偶数情况下,直接可以把所有的节点进行交换完全,但是技术情况下,剩余的节点只能保留不变。因此节点交换的终止条件就是cur->next,cur->next->next不能位空指针。原理示意图如下:
初始时,cur指向虚拟头结点,然后进行如下三步:
操作之后,链表如下:
更直观的展示交换后情况如下:
1.3附加代码如下所示:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode*dummyhead=new ListNode(0);
dummyhead->next=head;
ListNode*cur=dummyhead;
//理解循环截止条件是两种情况,一是偶数情况:cur->next,cur->next->next不能为空节点;二是奇数情况,生于一个节点无法进行交换保留下来不动
while(cur->next!=NULL&&cur->next->next!=NULL)
{
//把要破环的节点提前保留下来,不然后面无法进行更改节点
ListNode*temp=cur->next;
ListNode*temp1=cur->next->next->next;
//进行节点交换
cur->next=cur->next->next;
cur->next->next=temp;
cur->next->next->next=temp1;
//节点更新
cur=cur->next->next;//向后移动两位进行下一轮交换
}
return dummyhead->next;
}
};
2.LeetCode 删除链表的倒数第N个节点
2.1题目链接:19.删除链表的倒数第N个节点
2.2思路:要删除倒数第N个节点那么就找到该节点的前一个节点,采用双指针方法:倒数第N个节点删除,就让快慢指针初始时相差N个节点,然后再同时向后移动,但是这样的话遍历到终点时,慢指针指向的就是要删除的节点,无法得到该节点的前一个节点,所以为此我们需要初始时快慢指针相差N+1个节点,那么遍历到终点时慢指针就指向要删除结点的前一个节点了,可以进行删除操作了。
定义fast指针和slow指针,初始值为虚拟头结点,如图:
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
fast和slow同时移动,直到fast指向末尾,如题:
删除slow指向的下一个节点,如图:
2.3附加代码如下所示:
//采用双指针法:倒数第N个节点,就让快=快慢指针相差N个节点然后一起向后移动,直至到空节点,那么慢指针指向的为要删除的节点,
//但是要删除一个节点必须要得到该节点的前一个结点,于是应该让快指针在此基础上多移动一个节点,快指针应该初始相差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;
n++;//快指针与慢指针相差的节点数n+1
while(n--&&fast!=NULL) //此处应该写fast!=NULL,不能写fast->next!=NULL,不然会跳过一些节点
{
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
//要删除的节点
ListNode*temp=slow->next;
//更新删除后的节点指向
slow->next=slow->next->next;
//释放删除后的节点空间
delete temp;
return dummyhead->next;
}
};
3.LeetCode 环形链表II
3.1题目链接:142.环形链表II
3.2思路:环形链表首先是判断有没有环,然后才能去找环形的入口;采用快慢指针法。快指针一次走两个节点,慢指针一次走一个节点,这样如果有环形存在的话必有指针fast=slow,根据据推理公式如果有环形的话,相遇点必定满足X=Z条件,让另外两指针一个从起始点一个从相遇点出发知道二者指针相等则找到环形入口交点为当前指针。
fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
那么相遇时: 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)
两边消掉一个(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,
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
3.3附加代码如下所示:
//采用双指针法:慢指针一次走一个节点,快指针一次走两个节点
class Solution
{
public:
ListNode*detectCycle(ListNode*head)
{
//要考虑两点,一是环形是否一定存在,存在的话满足什么条件,存在的话快慢指针一定会再环里相遇
//二是存在环形的话在什么地方相遇以及环形入口在哪,根据推到公式可知,快慢指针相遇 之后,要找到环形入口
//满足起始点到入口距离等于相遇点到入口距离即X=Z
if(head==NULL)return NULL;
ListNode*slow=head;
ListNode*fast=head;
while(fast->next!=NULL&&fast->next->next!=NULL)//快慢指针移动截止条件
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)//快慢指针相遇点
{
ListNode*index1=head;
ListNode*index2=fast;
while(index1!=index2)
{
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}
};
4.LeetCode面试题.链表相交
4.1题目链接:02.07.链表相交
文章讲解:代码随想录
4.2思路:首先理解题目意思,链表相交不是数值相等就满足条件,二是指针相等才满足条件;链表长度不一致时候需要根据二者的长度差值N让长度长的链表先移动差值N位,然后再让二者同时移动若遇到链表指针相等则找到满足条件的交点。
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
4.3附加代码如下所示:
//思路:先求出两个链表的长度然后求出两个链表的长度差值N,再将长度长的链表向后移动N位之后,再让两个链表同时向后移动直到遇到两个链表指针相等,即找到相交点
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode*curA=headA;
ListNode*curB=headB;
int lenA=0;//链表A长度
int lenB=0;//链表B长度
int sub=0;//两个链表长度差值
while(curA)
{
curA=curA->next;
lenA++;
}
while(curB)
{
curB=curB->next;
lenB++;
}
//此时应该重新将headA和headB指向curA和curB的,因为在前面的遍历之后指针最终指向了NULL
curA=headA;
curB=headB;
if(lenA>lenB)
{
sub=lenA-lenB;
while(sub--&&curA!=NULL)
{
curA=curA->next;
}
}
else
{
sub=lenB-lenA;
while(sub--&&curB!=NULL)
{
curB=curB->next;
}
}
while(curA)//再次遍历curA和curB直到二者指针相等
{
if(curA==curB)
{
return curA;
}
curA=curA->next;
curB=curB->next;
}
return NULL;
}
};