24.两两交换链表中的节点
帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点
思路
只需要搞清楚当前操纵的是那个节点,理清楚什么时候该退出循环(当p的下个节点为空时,说明全部都交换完了),同时因为是两个节点两个节点处理的,所以链表节点个数为奇数的话,需要做一些单独的处理,即要最后一个节点插在处理完的链表尾。
代码实现:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
//尝试用两个指针交替插入头节点
ListNode* dummyhead=new ListNode(0);
ListNode* p=head;
if(head==nullptr||head->next==nullptr) return head;
ListNode* rear=dummyhead;//插入链表的尾指针
rear->next=nullptr;
while(p!=nullptr&&p->next!=nullptr){
ListNode* temp=p->next->next;
p->next->next=rear->next;
rear->next=p->next;
rear=p->next;
p->next=rear->next;
rear->next=p;
rear=p;//插入节点
p=temp;//移动节点指针
}
if(p!=nullptr){//如果是奇数个节点,最后一个节点需要单独处理,因为p->next为空的时候也会跳出循环,也就是循环相当于两个节点两个节点的处理,如果有奇数个节点,最后肯定会漏掉一个未处理,所以要单独处理。
p->next=rear->next;
rear->next=p;
rear=p;
}
head=dummyhead->next;
delete dummyhead;
return head;
}
};
时间复杂度o(n) 空间复杂度o(1)相当于对n个节点都进行了一次操作,所以时间复杂度是o(n)
19.删除链表倒数第N个节点
链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点
方法一
直接求出链表长度,然后算出来倒数第n个元素是正树的第几个元素
代码实现:
class Solution {
public:
int getlen(ListNode* head){
ListNode* p=head;
int count=0;
while(p!=nullptr){
count++;
p=p->next;
}
return count;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
//方法一,
int length = getlen(head);
int index = length-n;
ListNode* dummyhead=new ListNode(0);
dummyhead->next=head;
//找到待删除元素的位置
ListNode* cur = dummyhead;
for(int i=0;i<index;i++){
cur=cur->next;
}
//删除元素
ListNode* q=cur->next;
cur->next=q->next;
delete q;
head=dummyhead->next;
delete dummyhead;
return head;
}
};
时间复杂度o(n)空间复杂度o(1)
方法二
双指针法,因为要找倒数第N个节点,所以可以先让快指针移动N个节点,然后快慢指针同时移动,若快指针指向空,这时慢指针指向的节点即为待删除节点。但是由于删除节点需要该节点的前驱节点,所以可以判断快指针的下一个节点是否为空,如果下一个节点为空,那么慢指针指向的便是待删除元素的前驱节点,只需要进行常规的删除操作即可。
代码实现:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead=new ListNode(0);
dummyhead->next=head;
ListNode* fast=dummyhead;
ListNode* slow=dummyhead;
while(n--) fast=fast->next;//先让快指针移动n个节点
while(fast->next!=nullptr){
fast=fast->next;
slow=slow->next;
}
//这时慢指针指向待删除元素的前驱节点,接下来进行删除操作
ListNode* q=slow->next;
slow->next=q->next;
delete q;
head=dummyhead->next;
delete dummyhead;
return head;
}
};
时间复杂度o(n)空间复杂度o(1)
面试题02.07.链表相交
思路
要求相交的位置,所以如果两个链表相交,则从后往前数到相交位置的节点个数一定相同,所以两个链表的长度差从哪里产生? 肯定是相交之前的位置,所以只要求得两个链表的长度,长链表先遍历长度差个节点,然后短链表开始与长链表同时遍历,每遍历一个节点判断此时两个链表所指节点是否为同一个节点,如果是,那么返回,如果不是,那就两个链表没有相交节点。
代码实现:
class Solution {
public:
int getlen(ListNode *head){
ListNode *p=head;
int count=0;
while(p!=nullptr){
p=p->next;
count++;
}
return count;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA=getlen(headA);
int lenB=getlen(headB);
ListNode* pa=headA;
ListNode* pb=headB;
ListNode* res=nullptr;
if(lenA>lenB){
int cha=lenA-lenB;
while(cha--) pa=pa->next;
while(lenB--){
if(pa==pb) {
res=pb;
break;
}
pa=pa->next;
pb=pb->next;
}
}
else{
int cha=lenB-lenA;
while(cha--)pb=pb->next;
while(lenA--){
if(pa==pb) {
res=pa;
break;
}
pa=pa->next;
pb=pb->next;
}
}
return res;
}
时间复杂度o(n),空间复杂度o(1);
142.环形链表
把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表
思路:
如图, 定义快指针fast和慢指针slow,快指针每次跳两格,慢指针每次跳一格。所以如果链表有环的话一定是快指针先进入环,然后慢指针后进入环。
假设从表头到环入口处的距离为x,从入口处到相交的距离为y,从相交处到入口处的距离为z。
那么当快慢指针相交时,便可以根据快慢移动速度的不同列出两个等式(快慢指针各移动了多少距离):
慢:slow=x+y
快:fast=x+y+n(y+z)n代表快指针已经转了n圈了
因为2*慢=快
所以 2(x+y)=x+y+n(y+z)
要找环的入口,所以要找x和yz的对应关系,化解等式得x=n(y+z)-y,因为快指针和慢指针相遇的时候肯定在环里转过1圈以上了(至于为什么可以理解为慢指针被快指针套圈了,套圈意味着他肯定以及跑了一圈以上了),n又代表快指针转了多少圈,所以可以让快指针让出来一圈得到
x=(n-1)(y+z)+z
假设n为1时,x=z(n!=1其实也一样,只不过是快指针多转了几圈,然后最后一圈和n等于一一摸一样)这时就找到了x和z的关系,即相遇节点之后到环入口处的距离和表头到环入口处的距离一样
此外:
至于为什么慢指针入环第一圈就会被快指针追上,从上图便可以理解,从慢指针入环到相遇这个期间,快指针肯定会遇到慢指针,因为快指针的速度是慢的两倍。
代码实现:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow=head;
ListNode *fast=head;
while(fast!=nullptr&&fast->next!=nullptr){
fast=fast->next->next;
slow=slow->next;
if(slow==fast){//快慢指针相遇,此时从head和相遇点同时查找,因为距离一样
ListNode* index1=fast;
ListNode* index2=head;
while(index1!=index2){
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return nullptr;
}
总结
今天的第四题难度较大,涉及了数学推理,需要好好掌握,前面三题算是对知识点的巩固。