LeetCode——141. Linked List Cycle && 142. Linked List Cycle II

来源:互联网 发布:网络女主播直播间 编辑:程序博客网 时间:2024/06/06 07:25

  今天再次推出一套组合题。
  首先是:141.Linked List Cycle
  问题描述:

Given a linked list, determine if it has a cycle in it.Follow up:Can you solve it without using extra space?

  题目大致的意思就是给你一个链表,判断它里面是否含有环,有的话返回true,没有的话返回false。
  我们可以把这个链表抽象成一条直线加一个圆(如果它有环的情况下),如下图:

  
  
  对于这道题,我一开始的想法就是,保存你走过的点,然后当你再次走到你走过的点,不就可以证明链表有环咯。这种方法很简单,我就没写代码,用一个hashmap来保存点就可以实现了。
  但是,题目要求的是常数级的空间复杂度,说明我们不能通过保存状态的这种方法来实现了,得想另外一种方法。最近刚好要体育测试,我就想起与女朋友跑步的情形:她跑得慢,我跑得快,最后我跑多一圈还是会追上她,与她相遇。放在这里就是:如果有环的话,我用一个快指针(移动比较快的)在前面跑,一个慢指针(移动比较慢的)在后面跑,如果最后快指针能够与慢指针相遇,不就说明链表中有环了吗?问题迎刃而解。我写了两种代码,第一种是两者的速度是会不断变大的,他们之间速度的差距也会不断变大,这样子可以快一点到达环,但是在环中也要走比较多的路两者才能相遇;第二种是固定快指针的速度是慢指针速度的两倍,快指针一次走两步,慢指针一次走一步,这样子虽然比较慢到达环,不过在环中两者会很快就相遇。两者其实差别不大,第二种是我在做第二题的时候才写的,因为第二题就需要具体的量化了,所以必须要有一个准确的量。下面是代码:

    public boolean hasCycle(ListNode head) {        //方法1,两者的速度差距越来越大,最后也会相遇,不过可能会走了多余的路,效率低一点        ListNode fastP = head;        ListNode slowP = head;        int fastPath = 1;        int slowPath = 0;        while(slowP != null) {            //根据速度来移动快指针            for(int i = 1; i <= fastPath; i++) {                fastP = fastP.next;                if(fastP == null)                    return false;                if(fastP == slowP)                    return true;            }            //根据速度来移动慢指针            for(int i = 1; i <= slowPath; i++) {                slowP = slowP.next;                if(slowP == null)                    return false;                if(slowP == fastP)                    return true;            }            //因为快指针移动地比慢指针快,因此,快指针所移动的步数一定逐渐多余慢指针,才能显出两者速度的差距            slowPath += 1;            fastPath += 2;        }        return false;        //方法2,两者的速度定下来,快指针的速度是满指针的两倍        //那么实际上只要快指针比满指针走多一圈,两者就可以相遇了,不需要走冤枉路        ListNode fastP = head;        ListNode slowP = head;        while(fastP != null && fastP.next != null) {            fastP = fastP.next.next;            slowP = slowP.next;            if(slowP == fastP)                return true;        }        return false;    }

  第一题的解法就是这样,其实也不难。


  接下来就是第二道题:142. Linked List Cycle II
  问题描述:

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.Note: Do not modify the linked list.Follow up:Can you solve it without using extra space?

  这道题与上一道题差不多,只不过这道题是要找到环的起点,并返回。如果链表中不存在环就返回null。
  最开始依然是想到用hashmap来保存访问过的点,当你再次访问到已经访问过的点时,那个点就是起点,很容易理解,不过不满足常数级空间复杂度,代码如下:

    public ListNode detectCycle(ListNode head) {        //这是使用了额外空间的方法        Map<Integer, List<ListNode>> tempMap = new HashMap<>();        ListNode cur = head;        while(cur != null) {            if(!tempMap.containsKey(cur.val)) {                List<ListNode> tempList = new LinkedList<>();                tempList.add(cur);                tempMap.put(cur.val, tempList);            }            else {                for(ListNode p : tempMap.get(cur.val)) {                    if(p == cur)                        return cur;                }                tempMap.get(cur.val).add(cur);            }            cur = cur.next;        }        return null;    }

  当然了,我们肯定是要继续优化,实现常数级的空间复杂度。不过说实话,挺难想的。后来我也是直接看大神的解答,才发现,这里涉及到了一点简单的几何推导。我是真的没想到啊,写个算法题,简单的几何推导也弄出来了。下面就看我的图来跟着我的思路走吧,其实很简单的:
  
  这里写图片描述
  a是链表起点到环起点Y的距离,Z是快指针与慢指针第一次相遇的点,b是Y到Z的劣弧,c是Z到Y的优弧

  前方高能!!!
  根据快指针的速度是慢指针速度的两倍,有:
  2(a + b) = a + b + c + b
  a+b是慢指针在相同时间内走的距离,a + b + c + b是快指针在相同时间内走的距离
  两边相互消去,可以得到:
  a + b = b + c
  a = c!!!!!!
  这一个等式相当重要。它意味着,用两个相同速度的指针,一个从第一次相遇点出发,另一个从链表起点出发,两者第一次相遇的点,就是环的起点!!!问题不就解决了吗!!!
  下面是代码:

    public ListNode detectCycle(ListNode head) {        //不使用额外空间的方法,依旧是使用快慢指针,只不过这里限制了快指针的速度为慢指针速度的两倍        ListNode fastP = head;        ListNode slowP = head;        //这个循环是为了获取快指针和满指针的相遇点        while(true) {            if(fastP == null || fastP.next == null)                return null;            fastP = fastP.next.next;            slowP = slowP.next;            if(fastP == slowP)                break;        }        ListNode begin = head;        //根据公式,从相遇点出发与从链表起点出发,两者最终相遇的地方,就是起点        while(begin != fastP) {            begin = begin.next;            fastP = fastP.next;        }        return begin;    }

  这种解法真是太6了!有没有!我根本就没想到这种题目还能抽象成几何题来做,看来我还是太弱了。
  我觉得吧,以上两道题,都考了我们一种将具体的模型进行抽象的方法。如果在这两道题中,我们能够对这个模型进行抽象,然后运用到几何中一些简单的原理,问题是很容易解决的。所以有时我们需要将抽象的模型具象化,有时也需要将具体的模型抽象化,这也是一种很重要的思维。
  谢谢大家观看我的博客。如果有不明白的地方,或者是文中有错误的地方,欢迎指出,谢谢!如果大家喜欢我的博客,也可以给我点点赞。你们的点赞就是我的动力!

阅读全文
0 0
原创粉丝点击