目录
🌈前言
大家好呀🏠!我是耀 星,这里是数据结构刷题频道📻,如果你也在学习数据结构的话,不妨也跟着我一起学习学习📖,大家看见题目时可以先自己思考一下,不会的话可以来看我的图解分析,我一定努力用最直观的表示方式给大家呈现📃,也可以私信我一同交流交流👬,一起学习,私信必回!
Leetcode-反转单链表
OJ链接:反转单链表
💯解法1:图解分析
有如下链表
💡思路:将第N个结点指向它上一个结点,若该结点是第一个结点则指向NULL
我们要让第N个结点指向它上一个结点,我们需要知道第N-1个结点的位置📍,知道第N个结点的位置,知道第N+1个结点的位置📍(改变N的位置)。所以需要三个指针来保存三个结点的位置。
💭初始条件:
struct ListNode *node1=NULL, *node2=head, *node3=head->next;
💭迭代过程:
//将第N个结点与N-1个结点连接
node2->next=node1;
//改变N的位置
node1=node2;
node2=node3;
if(node3 != NULL)
node3=node3->next;
💭结束条件:第N个位置为NULL
😱特殊情况1:当链表为空时
if(head == NULL)
{
return NULL;
}
🙋完整解答1:
struct ListNode* reverseList(struct ListNode* head){
//特殊情况1
if(head == NULL)
{
return NULL;
}
struct ListNode *node1=NULL, *node2=head, *node3=head->next;//初始条件
while(node2 != NULL)//结束条件
{
node2->next=node1;//第N个结点连接第N-1个结点
//改变N的位置
node1=node2;
node2=node3;
if(n3 != NULL)
node3=node3->next;
}
head=n1;
return head;
}
💯解法2(头插法):图解分析
链表图如下:
思路:建立一条新的链表 ,依次将旧链表的元素头插插入新链表。
注意:当我们插入第N个结点时,该结点的指向域就发生改变,为了能完成每一个结点插入,我们需要先将第N+1个结点保存。
🙋完整解答2:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur=head, *newNode=NULL;
while(cur != NULL)
{
//保存第N+1个结点的位置
struct ListNode *next=cur->next;
//头插
cur->next=newNode;
newNode=cur;
//改变N的位置
cur=next;
}
return newNode;
}
对头插法还不了解的同学可以看:单链表基本操作及图解分析
🙋完整解答3(递归):
struct ListNode * myreverseList(struct ListNode *pre,struct ListNode *cur);
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL)
{
return NULL;
}
else
{
return myreverseList(NULL,head);//传递第一个结点,第一个结点指向NULL
}
}
struct ListNode* myreverseList(struct ListNode *pre,struct ListNode *cur){
//走到链表的最后一个结点时 返回链表头指针
if(cur == NULL)
{
return pre;
}
else
{
struct ListNode * temp= myreverseList(cur,cur->next);//记录头指针值
cur->next=pre; //依次将链表的第N个结点和第N-1个结点连接
return temp;
}
}
LeetCode-链表的中间结点
OJ链接:链表的中间结点
💯解法:图解分析
基本思路:快慢指针法
快指针:一次走两步
慢指针:一次走一步
当链表长度为奇数个时:
当链表长度为偶数个时 :依据题意,只返回一个结点,例如有六个结点返回第四个结点。
🙋完整解答:
struct ListNode* middleNode(struct ListNode* head){
struct ListNode *slow = head;
struct ListNode *fast = head;
while(fast != NULL && fast->next != NULL)//两个条件都需要满足
{
//快慢指针
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
leetCode-合并两个有序链表
OJ链接:合并两个有序链表
💯解法:图解分析
基本思路:分别遍历两个链表,依次将两个链表比较小的元素尾插进入新链表。
为了便于尾插提高效率:我们可以用一个tail指针来指向新链表的尾结点
🙌第一次插入新链表:
//第一次插入list1中的结点
newList=list1;
list1=list1->next;
tail=list1;
tail->next=NULL;
🙌 第二次插入新链表:
tail->next=list2;
list2=list2->next;
tail=tail->next;
tail->next=NULL;
🙌第三次插入新链表:
tail->next=list1;
list1=list1->next;
tail=tail->next;
tail->next=NULL;
🛑特殊情况1:初始化时新链表为空,直接将新链表指向第一个结点较小的链表。
🛑特殊情况2:当一条链表为空时,直接将另一条链表剩下的结点接到新链表的尾结点中。
🙋完整解答:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode *newList=NULL;
struct ListNode *tail=NULL;
//处理其中一条链表为空的情况
if(list1 == NULL)
return list2;
if(list2 == NULL)
return list1;
//提前给新链表一个结点,便于尾插
if((*list1).val > (*list2).val)
{
newList=list2;
list2=list2->next;
tail=newList;
tail->next=NULL;
}
else
{
newList=list1;
list1=list1->next;
tail=newList;
tail->next=NULL;
}
while(list1 != NULL && list2 != NULL)
{
if((*list1).val >(*list2).val )
{
tail->next=list2;
list2=list2->next;
tail=tail->next;
tail->next=NULL;
}
else
{
tail->next=list1;
list1=list1->next;
tail=tail->next;
tail->next=NULL;
}
}
//若一条链表已经全部连接在新链表上,将另一条来链表剩下的连接在新链表上
if(list1 !=NULL)
tail->next=list1;
if(list2 != NULL)
tail->next=list2;
return newList;
}
LeetCode-判断链表中是否有环
OJ链接:环形链表
💯解法:图解分析
基本思路:快慢指针法
结论:如果是环形链表,快指针会和慢指针在某一个结点相遇
慢指针:一次走两步
慢指针:一次走一步
🈸证明结论:
当慢指针进入环区域时,假设慢指针和快指针相差N的距离
移动一次 距离变为N-1
移动两次 距离变为N-2
移动三次 距离变为N-3
.......
当移动N次时距离变为0即相遇
🙋完整解答:
bool hasCycle(struct ListNode *head) {
struct ListNode *slow=head;
struct ListNode *fast=head;
while (fast != NULL && fast->next !=NULL)
{
slow =slow->next;
fast =fast->next->next;
if(fast == slow)
{
return true;
}
}
return false;
}
附加问题1:slow走一步,fast走两步一定会相遇,若slow走一步,fast走n步(n=2,3...),它们一定会在环中相遇吗?
我们以n=3为例:当慢指针进入环区域时,假设慢指针和快指针相差N的距离
当N=偶数时他们能相遇
移动一次 距离变为N-2
移动两次 距离变为N-4
移动三次 距离变为N-6
....
当N=奇数时他们不能够相遇
移动一次 距离变为N-2
移动两次 距离变为N-4
移动三次 距离变为N-6
....
最后距离变为 -1,也就是说fast在slow前面一位,他们的距离是环的长度C-1
若环的长度C-1还是奇数则永远也追不到。
LeetCode-求环形链表的入口点
OJ链接:环形链表
💯解法:图解分析
基本思路:快慢指针找到相遇点,引入一个新指针从头开始走,一个从快慢指针相遇点开始走,这两个指针会在入口相遇
相遇绝不是巧合,是命中注定🎯
😱由图可知:
相遇点-入口 C-X n=1,2...
slow走的距离:X + L
fast走的距离: L+X+nC
2X+2L = L+X+nC
结果为:nC-X=L
相遇点到入口的距离就等于L
🙋完整解答:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *meet=head;
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast != NULL && fast->next != NULL)
{
slow=slow->next;
fast=fast->next->next;
if(slow == fast)
{
while(1)
{
if(meet == slow)
{
return meet;
}
slow=slow->next;
meet=meet->next;
}//end while
}//end if
}//end while
return NULL;
}
🔚结束语
㊗感谢大家看到最后,相信屏幕前的你一定会有所收吧!
如果觉得你学到了,给个小爱心💖就是对我的最大支持🙅。
鄙人不才,如果有不好的地方可以私信告诉我,博主一定努力✊改进。