链表习题精讲02

问题三—环问题

环问题在这里插入图片描述
环问题的数据结构描述就是,在一个单向链表中存在多个节点的尾指针指向同一个非null的节点,对于环问题我们主要面临的问题有二。

1.是否有环

判断是否有环的方法有很多这里给出几种解法的Java实现代码

方法一:基本蛮力求解

正常思路如下,从第一个节点开始,令其为当前节点,然后看看其他节点的后继指针是否指向当前节点,如果存在这样的节点,那么就可以判断链表中存在环,否则,对链表中其余节点重复上述过程。
致命缺点:无法确定尾节点,从而会形成死循环

方法二:散列表
 public static boolean DoesLinkedListContainsLoop_01(ListNode head){
            Set<ListNode> set = new HashSet<>();
            ListNode node = head;
            while (node!=null){
                if (set.contains(node))
                    return true;
                else
                    set.add(node);
                node = node.getNext();
            }
            return false;
        }

此处的思路是这个样子的:首先,我们要有散列表的基本概念,然后做如下的算法:
①首先建立一个散列表,在这里建立的是Hashset原因是这玩意能主动去重
②其次,对每一个节点进行遍历,看下我在散列表中是否找到了这个节点,如果有的话,则说明我们里面有环
③如果没有的话,则把这个元素加入到散列表中去

研究:Hashset中使用的散列函数是什么:???母鸡,以后研究

方法三 Floyd图算法

这个需要用到一点数论的知识,但是仍然很简单

public static boolean DoesLinkedListContainsLoop_02(ListNode head){
        //if (head.ListLength()==1)return false;
        ListNode slowptr = head.getNext();
        ListNode fastptr = head.getNext().getNext();
        if (slowptr==fastptr)return true;
        while (slowptr!=fastptr){
            if (slowptr==null||fastptr==null)return false;
            slowptr=slowptr.getNext();
            fastptr=fastptr.getNext().getNext();
        }
        return true;
    }

这个地方的思路是,当一个链表如果有环的话,只要进入环中,两个指针就出不来了,因此,我们需要设置两个速度不同的指针进行操作,如果两者最终能够相遇,则说明有环的存在

2.环开始的位置

现在我们在这个基础上进行拓展:找到环开始的位置,同时优化我们的Print函数和GetLenth函数,那么环开始的位置如何确定呢?这里同样给出两个解决思路

方法一散列表
//method01--- hashtable
    public  static int WhereLinkedListLoop_01(ListNode head){
        Set<ListNode> set = new HashSet<>();
        ListNode currnode = head;
        ListNode node = head;
        ListNode node_01 = null;
        int count = 1;
        while (node!=null){

            if (set.contains(node)){
                node_01 = node;
                break;
            }

            else
                set.add(node);
            node = node.getNext();

        }
        while (currnode!=node_01){
            currnode = currnode.getNext();
            count++;
        }
        return count;

    }

基于散列表实现的节点位置查找,主要思路如下:
首先通过第一步查找确定是否有环,如果有环,记录下环开始的节点,然后从头开始遍历直到他们相等,时间复杂度为O(n),空间复杂度为O(m)。

方法二 Floyd算法

这家伙真是个神人…
算法的思路如下

 //method02 --- Floyd
    public  static int WhereLinkedListLoop_02(ListNode head){
        if (head==null)return 0;
        ListNode slowptr = head;
        ListNode fastptr = head;
        slowptr=slowptr.getNext();
        fastptr=fastptr.getNext().getNext();
        if (slowptr==fastptr)return 2;
        while (slowptr!=fastptr){
            if (slowptr==null||fastptr==null)return 0;
            slowptr=slowptr.getNext();
            fastptr=fastptr.getNext().getNext();
        }
        slowptr = head;
        int count = 1;
        while (slowptr!=fastptr){
            slowptr=slowptr.getNext();
            fastptr = fastptr.getNext();
            count++;
        }
        return count;
    }

这个需要用到一点点数论的知识,我们首先看到在这里插入图片描述
这里有几个限制条件:首先 slowptr的移动速度是1,然后fastptr的移动速度是2,因此当他们第一次相遇的时候此时fastptr移动的距离减去slowptr=nL 其中L为环的长度,n为圈数
且此时乌龟移动的距离一定是fastptr到表头的一半,因此此时,如果将slowptr放回头节点,则有由于此时slow和fastptr同样相差n
L,此时,只要将fastptr的移动速度也改为1,则他们始终相差nl,因此只要是他进入环,则由于他们相差nL,因此一定回直接相遇,此时该节点就是他们环开始的地方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值