判断环形链表

一、问题概述

        环形链表就是在链表中存在环的情况,如果出现了环形链表,在程序中可能造成死循环的情况,导致严重的损失,因此需要判断一个链表是否成了环。如果有条件,可以为链表的节点设置一个visit属性,记录每次访问的次数,访问过一次就加1,如果出现了节点的访问次数为2,说明一定存在环。但有时,我们并不需要为该操作去修改弥足珍贵的数据结构,下面我们采用三种方法讲解如何求解环形链表的问题

二、求解方法

        我们先假设链表的数据结构如下:(来源于Leetcode141题)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

一、基于循环次数判断的暴力方法

        如果一个链表不成环,那么它就是可以穷尽的,我们假定你的链表的节点的个数小于10^4个,如果比这个还多的话,也许你需要用到树型结构了,不然你的速度会比乌龟还慢。

        因此我们可以一直循环,循环的时候,要么是因为到达了指针的尾部NULL,要么是因为循环次数太多超越了节点的个数,循环结束时判断循环次数就可以了。
        C++代码如下:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==NULL){
            return 0;//没有环
        }
        struct ListNode * temp = head->next;
        int count=0;
        while(temp!=NULL&&count<100001){
           temp=temp->next;
           count++;
        }
        if(count>=100001){
            return 1;
        }
        return 0;
    }
};

二、借助Hash表

        每次访问节点的时候都判断是否在hash表中,如果没有就加入,如果存在就说明有环,如果没有环,一个节点只可能被访问一次,这和我们企图修改链表的数据结构去维护访问次数是等价的,只是以一种不影响节点内容的方式去完成。
        Java代码如下:

public class Solution{
    public boolean hasCycle(ListNode head){
        //利用hash表保存 看该数组是否被遍历过
        Set<ListNode> buf = new HashSet<ListNode>();
        while(head!=null){
            if(!buf.add(head)){
                return true;
            }
            head=head.next;
        }
        return false;
    }
}

三、快慢指针 

        其实到这里,才是本问题最精彩的解法(前面都是在扯淡),这个方法十分精巧,是伟大的数学家Floyd发明的,即快慢指针的解法。

        基于Floyd环的思想,定义两种类型的指针,slow慢指针前进步长为1,快指针前进步长为2(在第一次前进后要判断前进后是否到达了NULL)。这样,如果在循环的过程中,快指针追上了慢指针,那么说明成了环。其中为什么一定能追上的具体的数学证明需要沉下心来仔细Google。

        C语言代码如下:

bool hasCycle(struct ListNode *head) {
    if(head==NULL){
        return 0;//不成环
    }
    //快慢指针
    struct ListNode * slow = head;
    struct ListNode * fast = head;
    while(fast!=NULL){
        //fast比slow快 只需要判断fast有没有越界就可以了
        slow = slow->next;
        fast = fast->next;
        if(fast!=NULL){
            fast=fast->next;//避免操纵空指针
        }
        else{
            break;
        }
        //放在后面 处理初始状态
        if(slow==fast){
            return 1;//成环
        }
    }
    return 0;
}

        此外,为什么笔者要这么蛋疼写三种解法和三种不同的语言求解呢?其实本人就是从菜鸡算法一步步向大佬算法挖掘的,只有一步一步走,才能到达幸福的彼岸。同时,用多种语言也旨在说明,算法是一种思想,语言只是一种工具。

        多谢大家指正,共勉! 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值