【数据结构刷经典题-C】-链表

目录

Leetcode-反转单链表

LeetCode-链表的中间结点

leetCode-合并两个有序链表

 LeetCode-判断链表中是否有环

 LeetCode-求环形链表的入口点


🌈前言

大家好呀🏠!我是耀 星,这里是数据结构刷题频道📻,如果你也在学习数据结构的话,不妨也跟着我一起学习学习📖,大家看见题目时可以先自己思考一下,不会的话可以来看我的图解分析,我一定努力用最直观的表示方式给大家呈现📃,也可以私信我一同交流交流👬,一起学习,私信必回!

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;
}

 🔚结束语 

㊗感谢大家看到最后,相信屏幕前的你一定会有所收吧!

如果觉得你学到了,给个小爱心💖就是对我的最大支持🙅。

鄙人不才,如果有不好的地方可以私信告诉我,博主一定努力✊改进。

评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南 栀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值