双向指针的应用

1.双向指针在链表中的应用

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

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

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

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

        下面是几道相关的题目:

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

  

[java] view plain copy
 
  1. public boolean hasCycle(ListNode head) {  
  2.         if(head == null){  
  3.             return false;  
  4.         }  
  5.         ListNode fast = head, slow = head;  
  6.   
  7.         while(fast != null && fast.next!=null){  
  8.             slow = slow.next;  
  9.             fast = fast.next.next;  
  10.             if(slow == fast){  
  11.                 return true;  
  12.             }  
  13.         }  
  14.         //注意循环的条件是fast不为空,并且fast.next不为空  
  15.         //若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true  
  16.         return head.next == head;  
  17. }  

       

 

       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一定是环的整数倍)。

        

[java] view plain copy
 
  1.  public ListNode detectCycle(ListNode head) {  
  2.         //如果有环,则两者一定是在环中相遇  
  3.         ListNode meetNode = meetNode(head);  
  4.         if(meetNode == null){  
  5.             return null;  
  6.         }  
  7.         ListNode slow = head;  
  8.         //当两者再次相遇时,即为链表中环的入口  
  9.         while(slow != meetNode){  
  10.             slow = slow.next;  
  11.             meetNode = meetNode.next;  
  12.         }  
  13.         return slow;  
  14.  }  
  15.  public ListNode meetNode(ListNode head){  
  16.         if(head == null){  
  17.             return null;  
  18.         }  
  19.         ListNode meetNode;  
  20.         ListNode fast = head, slow = head;  
  21.         while(fast != null && fast.next != null){  
  22.             slow = slow.next;  
  23.             fast = fast.next.next;  
  24.             if(slow == fast){  
  25.                 meetNode = slow;  
  26.                 return meetNode;  
  27.             }  
  28.         }  
  29.         //注意循环的条件是fast不为空,并且fast.next不为空  
  30.         //若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true  
  31.         return head.next == head ? head : null;  
  32.  }

        其他解法可以见这个链接,双指针中环的入口

 

        双指针的应用还有还多,除了可以应用在链表中,还可以用在数组中。比如求两个数的和等于给定的数时,就可以使用双指针来解决。以后会仔细谈一谈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的代码

 

[java] view plain copy
 
  1. public boolean isHappy(int n) {  
  2.         int slow = n , fast = n;  
  3.         do{  
  4.             slow = digitSquareSum(slow);  
  5.             fast = digitSquareSum(fast);  
  6.             fast = digitSquareSum(fast);  
  7.         }while(slow != fast);  
  8.         return slow == 1;  
  9.     }  
  10.       
  11.     public int digitSquareSum(int n){  
  12.         if(n < 0){  
  13.             return -1;  
  14.         }  
  15.         int sum = 0;  
  16.         while(n > 0){  
  17.             sum += Math.pow(n % 10 , 2);  
  18.             n = n / 10;  
  19.         }  
  20.         return sum;  
  21.     }  


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

另一个具体的题判断链表中是否存在环并且返回环的入口,具体如下:

Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull.

Follow up:
Can you solve it without using extra space?

代码如下:

原文地址:https://www.cnblogs.com/upcwanghaibo/p/6522542.html