环形链表C/C++数据结构

目录

什么是环?

例题 141. 环形链表

公式推导:

环形链表二  142. 环形链表 II

方法一:

方法二:

什么是环?

类似这种,结点下一个指针又指向链表本身,这样若遍历链表的话则会变成死循环。

 特殊情况:自己也有可能成环

 环的特点:

有两个指针指向环的起点

判断是否有环

例题 141. 环形链表

使用快慢指针,进入了环就变成了追及相遇的问题

 

 思路:快指针一次走两步,慢指针一次走一步,当两个指针相遇时,即在链表中存在环

 问题:为什么是快指针一次走两步?慢指针一次走一步?

bool hasCycle(struct ListNode *head) {
    //判断空链表
    if(!head){
        return NULL;
    }

    struct ListNode *slow=head;
    struct ListNode *fast=head;
    //判断条件是什么?
    //如果两个指针相等则有一个环
    //就把它当作一个正常数组想
    //因为只有这样才能从循环中出去
    //判断条件怎么想?
    while(fast&&fast->next)){
        fast=fast->next->next;
        slow=slow->next;
        //要是环的话根本出不去
        if(fast==slow){
            return true;
        }
    }
    return false;
}

公式推导:

假设链表中无环

快指针走的距离=2*慢指针走到距离

假设链表中有环

进环前的距离L,环的长度C,环的开始点至相遇点的距离X

 

慢指针走过的距离:L+X

快指针走过的距离:L+C*N+X

由此可以得出一个等式

L+X=N*C

L=(N-1)*C+C-X

意思是在入环前的长度==环的长度-(开始点至相遇点的距离)

也就是说: 蓝色部分=L

环形链表二

方法一:

本题首先需要找到相遇点,然后设立两个新的指针,两个指针从相遇点和起始点一起走,两个指针相遇的地方即为环形链表的起始点。

struct ListNode *detectCycle(struct ListNode *head) {
    //L=C-X
    //创立快慢指针
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    //确保fast没有空指针
    while(fast&&fast->next){
        //两者一起向前走
        slow=slow->next;
        fast=fast->next->next;
        //找到相遇
        if(slow==fast){
            struct ListNode *meet=slow;
            while(meet!=head){
                //找到相遇点,两者一起走
                meet=meet->next;
                head=head->next;
            }
            //相遇,返回环的起始点
            return meet;
        }
    }
    return NULL;
}

方法二:

使用相交链表的方式

先将链表从快慢指针的相遇点处断开

之后新建两个指针分别指向相遇点的下一位和原链表的头

利用相遇链表

相遇点即为起始点

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //想一想,极端情况
    //首先应该判断这两个链表不为空
    if(!headA||!headB)
        return NULL;
    
    //找到两个链表的最后一个节点
    struct ListNode *lastA=headA;
    int numA=1;
    while(lastA->next)
    {
        lastA=lastA->next;
        numA++;
    }

    struct ListNode *lastB=headB;
    int numB=1;
    while(lastB->next)
    {
        lastB=lastB->next;
        numB++;
    }
    //判断是否相交
    //这就是找最后一个节点的作用
    if(lastA!=lastB)
    {
        return NULL;
    }
    //找到长度,找最长的那个
    //不是说不能倒着走吗?难道是要它倒叙?
    //理解错了理解错了
    struct ListNode *lenglist=headA;
    struct ListNode *shortlist=headB;
    if(numA<numB)
    {
        lenglist=headB;
        shortlist=headA;
    }

    //最长的先走
    int distance=abs(numA-numB);
    while(distance--)
    {
        lenglist=lenglist->next;
    }

    //两个一起走
    while(lenglist!=shortlist)
    {
        lenglist=lenglist->next;
        shortlist=shortlist->next;
    }

    //走到不相等时分开,返回相交的节点
    return lenglist;
}

struct ListNode *detectCycle(struct ListNode *head) {
    //L=C-X
    //创立快慢指针
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    //确保fast没有空指针
    while(fast&&fast->next){
        //两者一起向前走
        slow=slow->next;
        fast=fast->next->next;
        //找到相遇
        if(slow==fast){
            struct ListNode *meet=slow;
            struct ListNode*next=meet->next;
            struct ListNode*walk=meet->next;
            meet->next=NULL;
            struct ListNode *newNode=getIntersectionNode(head,walk);
            meet->next=next;
            return newNode;
        }
    }
    return NULL;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值