剑指Offer Java版 双指针在链表中的应用

来源:互联网 发布:mac存储空间清理 编辑:程序博客网 时间:2024/06/06 06:34

       所谓双指针,指的是在遍历对象的过程中,不是使用单个指针进行访问,而是使用两个相同方向或者相反方向的指针进行遍历,从而达到相应的目的。双指针的使用可以降低程序的时间复杂度或者空间复杂度,总之是一种有效的解决问题的方案。

       (注:这里所说的指针,并不是C/C++中指针的概念,而是指索引,游标或指针,可迭代对象等)

       双指针在链表中也有很多用处,比如前面写到过的找出链表中的倒数第k个结点,就巧妙地利用到了双指针,此外,判断链表中是否有环也可以使用双指针,设两个快慢指针,让快指针一次移动两步,慢指针一次移动一步,若链表中有环,那么快指针与慢指针一定能够相遇,若两者没有相遇,说明链表中没有环。还有如果要给出链表中的中间的结点,也可以使用快慢指针,让快指针一次移动两步,慢指针一次移动一步,当快指针刚好到达链表的末尾时,慢指针所指向的正好是中间的结点(对于奇数个结点,就是中间中的一个,若是偶数个结点,是中间结点中的后一个,即length / 2);

       由此看出,双指针的思想就是建立两个指针,这两个指针可以使相同方向,一般前进的速度不同或者两者的前进顺序不一致;也可能是相反的方向,通过使用相关的变量控制来达到我们的目的

        下面是几道相关的题目:

         1.给出一个链表,判断是否有环

  

public boolean hasCycle(ListNode head) {        if(head == null){            return false;        }        ListNode fast = head, slow = head;        while(fast != null && fast.next!=null){            slow = slow.next;            fast = fast.next.next;            if(slow == fast){                return true;            }        }        //注意循环的条件是fast不为空,并且fast.next不为空        //若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true        return head.next == head;}
       

       2.给出一个链表,若链表中有环,给出环的入口结点,否则返回null

         链表中有环的结构如下图所示(图片来自http://www.nowamagic.net/librarys/veda/detail/2245)

           这里写图片描述

       若链表中有环,则采用快慢指针判断时,两者一定在环内相遇(除非环的入口在头结点)。推导过程如下:

       如果判断出单链表有环,如何找到环的入口?比如上图中,D就是环的入口。假设慢指针移动了S个结点,则快指针移动了2S个结点,如果环入口离头结点的距离为x,环入口离相遇的那个结点距离为y,环的长度为r, 单链表总长度为L,他们的关系推导如下:

  • 2S = S + n * r (快指针的步数等于S 加上环上多转的n圈)
  • S = n * r
  • S = x + y = n * r
  • x + y = (n - 1) * r + r
  • x + y = (n - 1) * r + L - x
  • x = (n - 1) * r + L - x - y

    最后一个式子中,L - x - y为相遇点到环入口的距离,最后一个式子表明环入口到头结点的距离等于(n - 1) * r 加上相遇点到环入口的距离。也就是说一个指针从头结点出发,另一个指针从相遇点出发,步长都为1的话,这两个指针一定会在环入口相遇(因为 r为环的长度,(n - 1) * r一定是环的整数倍)。

        

<span style="font-family:microsoft yahei;"> public ListNode detectCycle(ListNode head) {        //如果有环,则两者一定是在环中相遇        ListNode meetNode = meetNode(head);if(meetNode == null){return null;}        ListNode slow = head;        //当两者再次相遇时,即为链表中环的入口        while(slow != meetNode){            slow = slow.next;            meetNode = meetNode.next;        }        return slow; } public ListNode meetNode(ListNode head){        if(head == null){            return null;        }        ListNode meetNode;        ListNode fast = head, slow = head;        while(fast != null && fast.next != null){            slow = slow.next;            fast = fast.next.next;            if(slow == fast){                meetNode = slow;                return meetNode;            }        }        //注意循环的条件是fast不为空,并且fast.next不为空        //若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true        return head.next == head ? head : null; }</span>
        其他解法可以见这个链接,双指针中环的入口

        双指针的应用还有还多,除了可以应用在链表中,还可以用在数组中。比如求两个数的和等于给定的数时,就可以使用双指针来解决。以后会仔细谈一谈2sum(两个数的和),3sum,4sum问题以及适用于双指针的其他问题。

     2016 年10月 8 号更新

    今天刷leetcode的时候,碰到了一道happy number的题

A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.

刚开始以为是逐渐的判断,后来看了解析之后才明白我们同样可以检测是否有环(类似于链表中检测有环的思路),可以看这篇文章

下面是Ac的代码

public boolean isHappy(int n) {        int slow = n , fast = n;        do{            slow = digitSquareSum(slow);            fast = digitSquareSum(fast);            fast = digitSquareSum(fast);        }while(slow != fast);        return slow == 1;    }        public int digitSquareSum(int n){        if(n < 0){            return -1;        }        int sum = 0;        while(n > 0){            sum += Math.pow(n % 10 , 2);            n = n / 10;        }        return sum;    }

不得不说,之前以为检测环只能用于链表中,也没想过还可以这样使用,算是打开了思路吧


        




0 0
原创粉丝点击