首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >链表OJ题讲解---试金石含金量

链表OJ题讲解---试金石含金量

作者头像
胖咕噜的稞达鸭
发布2025-10-22 14:55:11
发布2025-10-22 14:55:11
2300
代码可运行
举报
文章被收录于专栏:C++初阶高阶C++初阶高阶
运行总次数:0
代码可运行

1. 环形链表(详细讲解)

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) 
//判断以head为头节点的单链表是否存在环
{
    ListNode* slow=head,*fast=head;
    while(fast && fast->next)//这是快指针,最先到达链表的末尾,当快指针指向为空,就说明没有环
    {
        slow=slow->next;
        fast=fast->next->next;

        if(fast==slow)
        return true;/如果fast==slow,说明链表是有环的
    }
    return false;//当fast都走到空了,还是没有出现fast==slow的情况,说明链表没有环
}

这道题我们还是使用快慢指针的方法,快指针走两步,慢指针走一步,如果链表是有环的,快指针会在环中追上慢指针。

也就是说,通过快慢指针的追击,若能相遇则链表有环;若快指针先走到链表末尾,则无环。

这里我们来看一下画图的解释。

1.1 环形链表面试拓展问题证明!!!

1.1.1 说明为什么会相遇,有没有可能会错过,永远追不上?

这里我们还是画图分析。

slow⼀次走⼀步,fast⼀次⾛2步,fast先进环,假设slow也走完⼊环前的距离,准备进环,此时fast和slow之间的距离为N,接下来的追逐过程中,每追击⼀次,他们之间的距离缩小1步。

总结一下这个问题,当环的周长为C的时候,当fast 和 slow相差N步,N为偶数的时候,第一轮一定可以追上,但是N为奇数的时候,第一轮追不上,而距离就会变成C-1。

如果C是奇数,那么C-1就是偶数,第二轮一定可以追上;如果C是偶数,那么C-1就是奇数,就追不上,而且永远都追不上。

1.1.2 slow一次走1步,fast一次走3步 4步 5步 n步,请证明

fast走3步追上,N%3 == 0             fast走4步,N%3==1                   fast走3步追上,N%3==2

N-3                                       N-3                                                          N-3  

N-6                                       N-6                                                          N-6

3                                           1                                                                2

0                                           -2(C-2)                                                -1(C-1)

反思:同时存在N是奇数而且C是偶数的情况,那么永远追不上这个假定是正确的吗,真的会存在永远都追不上的情况吗?

slow走的距离是:L; fast走的距离是:L+x * C+C-N(假设走了x圈)

slow进环的时候,假设fast已经在环里面转了x圈

这里列等式,3L=L+x * C+C-N;化简完成为2L=(x+1)* C-N

偶数=(x+1)* 偶数-奇数

只有奇数-奇数才等于偶数,但是(x+1)*偶数一定是偶数,所以反证出N是奇数而且C是偶数的情况不可能同时出现,永远追不上的条件是不成立的。

结论:一定是可以追上的,N是偶数第一轮就追上了;

                        ​​​​​​​        ​​​​​​​    N是奇数第一轮追不上,C-1是偶数第二轮就追上了

这里需要一定的数理知识,所以需要耐心推导,曾经面试考官考题,重视!!!

1.1.3 求环的入口节点

题目要求:判断链表是否有环,并且如果有环的话,找到环的入口节点。

N(假设slow进环走了N步,就跟fast相遇);

环的周长是C;

快慢指针在进环之前走了L步

slow走的路程:L+N fast走的路程:L+x*C+N

fast走的路程是slow的2倍, 2(L+N)=L+x*C+N 化简:L=x* C-N===>L=(x-1) * C+C-N,x >=1 而且是整数

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* slow=head,*fast=head;
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;

        //相遇
        if(fast==slow)
        {
            ListNode* meet=slow;//定义一个指针meet,此时slow所在的位置是快慢指针在环内的相遇点
            while(meet != head)//head指针从链表的头节点出发,meet从快慢指针在环内的相遇点出发
            {
                meet=meet->next;
                head=head->next;
            }
            return meet;//相遇的节点就是链表环的入口节点
        }
    }
    return NULL;
}

2.随机链表的复制

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
	Node* cur=head;
    //拷贝节点入原节点的后面
    while(cur)
    {
        //为拷贝的节点申请空间
        Node* copy=(Node*)malloc(sizeof(Node));
        copy->val=cur->val;//先复制数值

        copy->next=cur->next;//把拷贝节点copy插入原节点cur的后面
        cur->next=copy;//让拷贝节点的next指向原节点的下一个节点

        cur=copy->next;//让原节点的next指向拷贝节点
    }
    //控制random
    cur=head;//重新开始遍历链表
    while(cur)
    {
        Node* copy=cur->next;//获取cur对应的拷贝节点copy
        if(cur->random==NULL)
        {
            cur->random=NULL;
        }
        else
        {
            copy->random=cur->random->next;//copy的random指向该原节点random所指向的节点对应的拷贝节点,因为原节点的next就是其对应的拷贝节点
        }
        cur=copy->next;//继续向后循环
    }
    //把拷贝节点取下来尾插成为新链表,然后恢复原来的链表(不恢复也可以)
    Node* copyhead=NULL,* copytail=NULL;
    cur=head;//再次遍历
    while(cur)
    {
        Node* copy=cur->next;//获取当前原节点cur对应的拷贝节点copy
        Node* next=copy->next;//用与后期恢复链表和遍历原链表

        if(copytail==NULL)
        {
            copytail=copyhead=copy;
        }
        else{
            copytail->next=copy;//copy的值尾插
            copytail=copytail->next;//向后移动
        }

        cur->next=next;//用于恢复原链表
        cur=next;//将cur继续向后移动
    }

    return copyhead;//返回拷贝链表的头节点
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 环形链表(详细讲解)
    • 1.1 环形链表面试拓展问题证明!!!
      • 1.1.1 说明为什么会相遇,有没有可能会错过,永远追不上?
      • 1.1.2 slow一次走1步,fast一次走3步 4步 5步 n步,请证明
      • 1.1.3 求环的入口节点
  • 2.随机链表的复制
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档