数据结构:链表典例

目录

一、环形链表

1.判断链表是否成环

2.基于上题,判断是否成环,若成环则返回第一个成环的节点,否则返回NULL

方法一:数理证明

方法二:转化为链表相交(但本题要求不改变链表结构,所以代码仅供大家参考)

二、复杂链表的深度拷贝

思路:


一、环形链表

1.判断链表是否成环

题目描述:给一个链表头指针,设计一个算法,如何判断其是否成环,成环返回true,否则返回false

. - 力扣(LeetCode)

思路分析:采用快慢指针方式,慢指针一次一步,快指针一次走两步,如果成环,由于快指针走得快,最终它们必然在环中相遇;如果不成环,快指针最终为空。分别判断返回即可

bool hasCycle(struct ListNode* head) {
    struct ListNode *fast = head, *slow = head;
    if (head == NULL) {
        return false;
    }
    while (fast && fast->next) // 循环条件要使链表不成环时能够跳出,必须两个条件同时满足,因为fast一次走两步,不成环有两种情况,fast为最后一个节点,fast为最后一个节点的指针域(即NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (slow == fast) {
            return true;
        }
    }
    return false;
}

-->fast能否一次走三步,此时能否相遇,此情况下存在永不相遇的情况吗?

当慢指针进入环时,不妨假设fast与slow相距N,进行如下分析:

显然如果N为偶数,则快慢指针最终能相遇,而如果N为奇数,则slow进入时fast的第一轮追击将错过,但我们不能立刻断定此情况下不会相遇,因为还存在第二轮,甚至第三轮追击。

接下来对第二轮追击进行分析:(注意之所以有第二轮是因为N为奇数)

不妨假设环的长度为C,则第二轮追击开始时,fast与slow相距C-1,根据前面分析知道,如果C-1为偶数,则能相遇,如果C-1为奇数,则又将错过而陷入死循环中,因此问题在于C-1为偶数还是奇数。

由于N为奇数,则C不可能为偶数,否则等式不可能成立,即C必然为奇数,所以C-1必然为偶数,故第二轮必然能相遇。

CL:fast能够一次走三步,无论N为奇数还是偶数,最终都能相遇。区别在于N为偶数,追一轮相遇,N为奇数,追两轮相遇。

-->fast能否一次走更多步呢?方法类似,不过更为复杂,大家可以自行分析

2.基于上题,判断是否成环,若成环则返回第一个成环的节点,否则返回NULL

方法一:数理证明

同样方法判断是否成环,成环则快慢指针相遇,此时给出一指向头节点的指针cur,让cur和slow同时移动,一次一步,它们必然相遇,且相遇时节点为第一个成环节点,返回即可。

接下来证明一下cur和slow相遇节点为第一个成环节点

代码如下:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode*fast=head,*slow=head;
    struct ListNode*cur=head;
    while(fast && fast->next)
    {
        fast=fast->next->next;//快指针一次走两步
        slow=slow->next;//慢指针一次走一步
        if(fast==slow)//如果成环必然在环中相遇
        {
            while(cur!=slow)//相遇跳出
            {
                cur=cur->next;
                slow=slow->next;
            }
            return cur;//返回第一个成环节点
        }
    }
    return NULL;
}

方法二:转化为链表相交(但本题要求不改变链表结构,所以代码仅供大家参考)

相遇时将链表断开,将相遇节点认为是新的头节点newhead,将相遇节点的前一个节点的指针域置空,由此可得到两个相交的单链表,返回相交链表第一个节点即为成环的第一个节点

代码如下:

二、复杂链表的深度拷贝

注意深度拷贝意味着一模一样,拷贝链表random要对应指向拷贝链表中的节点

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=O83Ahttps://leetcode.cn/problems/copy-list-with-random-pointer/description/

思路:

1.遍历原链表,逐个拷贝并链接在对应节点后面,建立拷贝链表和原链表的联系

2.处理random,根据拷贝链表random指向必然在原链表random指向的后一个

3.将拷贝节点挨个摘下链接在一起,并恢复原链表(也可不恢复)

/**
 * 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* cur1=head,*cur2=head,*cur3=head;
//挨个拷贝并链接在对应节点后面
    while(cur1)
    {
        Node* newnode=(Node*)malloc(sizeof(Node));
        newnode->val=cur1->val;
        newnode->next=cur1->next;
        cur1->next=newnode;
        cur1=cur1->next->next;
    }
    //处理random
    while(cur2)
    {
        if(cur2->random==NULL)//random为空需要格外注意
        {
        cur2->next->random=NULL;
        cur2=cur2->next->next;
        }
        else
        {
        cur2->next->random=cur2->random->next;
        cur2=cur2->next->next;
        }
    }
    //再将拷贝链表从原链表上摘下来尾插在一起
     Node* copytail=NULL,*copyhead=NULL;
    while(cur3)
    {
        Node* newnode=cur3->next;
        Node* next=newnode->next;
        if(copytail==NULL)
        {
            copyhead=copytail=newnode;
        }
        else{
            copytail->next=newnode;
            copytail=newnode;
            copytail->next=NULL;
        }
        cur3->next=next;//恢复原链表
        cur3=next;
    }
    return copyhead;//返回拷贝链表头节点
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

好想当只懒羊羊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值