为了方便题目不再赘述,直接发链接,依旧C语言
一、两两交换链表中的节点
1、力扣题目
2、初次写题
使用循环遍历,两两交换
代码如下:
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
// //设一个虚拟头节点
struct ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
struct ListNode *cur=head;
struct ListNode *pre=shead;
while(cur!=NULL&&cur->next!=NULL){//条件不能够写反,先判断第一个再判断第二个
//临时指针tmp
struct ListNode *tmp=cur->next;
//两两交换
cur->next=tmp->next;
pre->next=tmp;
tmp->next=cur;
//更新cur和pre
pre=cur;
cur=cur->next;
}
return shead->next;
}
还可以写成递归,代码如下:
struct ListNode* swap(struct ListNode* pre, struct ListNode* cur,struct ListNode* shead){
//返回
if(cur==NULL||cur->next==NULL)return shead->next;
//临时指针tmp
struct ListNode *tmp=cur->next;
//两两交换
cur->next=tmp->next;
pre->next=tmp;
tmp->next=cur;
//递归调用
return swap(cur,cur->next,shead);
}
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
// //设一个虚拟头节点
ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
return swap(shead,head,shead);
}
3、视频学习
视频思路跟上述想法差不多就不再放了。
4、总结
该题需要注意的是在进行交换的时候最好能够手动画一下图,以防止混淆。
二、删除链表倒数第N个节点
1、力扣题目
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
2、初次解题
想法是先循环找有一共有多少个节点,然后在循环找倒数第n个节点,进行删除。
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
typedef struct ListNode ListNode;
//虚拟头节点
ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
shead->next=head;
ListNode *cur=shead;
//统计节点数
int i=0;
while(cur->next!=NULL){
cur=cur->next;
i++;
}
//cur指向倒数第n+1个节点
cur=shead;
while(i-n!=0){
cur=cur->next;
i--;
}
//释放倒数第n个节点
ListNode *tmp=cur->next;
cur->next=tmp->next;
free(tmp);
return shead->next;
}
3、视频学习
视频地址:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili
思路解析:
(1)首先设定一个虚拟头节点和快慢指针。
(2)该题可抽象为移动一个固定长度(长度为n)的窗口,当窗口右边指向null时,slow指向的即使倒数第n个节点。
(3)不过需注意,释放节点时需要知道前后节点,所以初始时让fast为head,此时slow的next就是需要释放的节点。
代码如下:
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
typedef struct ListNode ListNode;
//虚拟头节点
ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
shead->next=head;
//快慢指针
ListNode *slow=shead;
ListNode *fast=shead->next;
//滑动窗口,长度为n
while(n--){
fast=fast->next;
}
while(fast!=NULL){
fast=fast->next;
slow=slow->next;
}
//释放倒数第n个节点
ListNode *tmp=slow->next;
slow->next=tmp->next;
free(tmp);
return shead->next;
}
三、链表相交
1、力扣题目
面试题 02.07. 链表相交 - 力扣(LeetCode)
2、写题和学习
思路跟我初次思路一致,只不过后面再次从头开始的时候我用了cur1代表长链表head,cur2代表短链表head,多花了无用的空间,下次可以直接重用。
直接上代码:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode *a=headA;
ListNode *b=headB;
int size_a=0,size_b=0,n;//n为链表之间的大小差
//查看最后一个节点,并且统计节点数(如果有节点的话比实际数小1)计算n不影响
while(a!=NULL&&a->next!=NULL){
a=a->next;
size_a++;
}
while(b!=NULL&&b->next!=NULL){
b=b->next;
size_b++;
}
//最后一个节点数不同,则不相交
if(a!=b)return NULL;
//计算n,此时a为长,b为短
if(size_a>=size_b){
a=headA;b=headB;
n=size_a-size_b;
}else{
a=headB;b=headA;
n=size_b-size_a;
}
//移动长链表head,使得距离交汇节点距离相同。
while(n--){
a=a->next;
}
//找交汇节点
while(a!=b){
a=a->next;
b=b->next;
}
return a;
}
这里提一下官方解析中的双指针法,该解法太巧妙了,该解法的思路就是,让两个指针走m+n的长度,最后一定会重合。如图所示,假如公共段为黑色,则按上述走法,一定会出现a=b的情况,如果没用公共段,那么最后就会一起走到null,此时a=b正好返回null。
四、环形链表
1、力扣题目
2、视频学习
视频地址:
把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili
思路解析:相当于一个横向有助跑道(长为x)的操场,从助跑道开始,一个跑的快,一个跑的慢,如果操场是环型跑道,那么快的一定会跟慢的重合,否则不是环型跑道。
当重合的时候,快的人已经跑了:x+n圈+距环入口正向距离y,慢的跑了:x+(n-m)圈+距环入口正向距离y,跑完一圈还剩z距离。此时的x=z(看视频的教学自己手动推导一下)所以只需要,其中一个指针从头开始,一个指针从重合处,同时以相同速度前进,重逢的时候就是环入口处。
struct ListNode *detectCycle(struct ListNode *head) {
typedef struct ListNode ListNode;
//虚拟头节点
ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
shead->next=head;
//快慢指针
ListNode *fast=shead;
ListNode *slow=shead;
while(fast->next!=NULL&&fast->next->next!=NULL){//是否循环
fast=fast->next->next;
slow=slow->next;
if(fast==slow){//循环则找入口
slow=shead;//从头和重合处,同时以相同速度前进
while(fast!=slow){
slow=slow->next;
fast=fast->next;
}
return fast;
}
}
return NULL;
}
3、总结
该题的思路十分值得学习,应该多看一下。注意重合时推导出来x=z(可以假设fast每次移动2,slow移动1,根据重合时fast走的路程为2倍的slow的路程进行推导)