zoukankan      html  css  js  c++  java
  • 双向指针的应用

    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?

    代码如下:

  • 相关阅读:
    JAVA实现WEBSERVICE 上传下载
    C# 扩展方法
    C# winform窗体假死
    javascript webstorm用法
    设计模式 单例模式
    c# 浏览器区别
    C# 文件递归
    批处理
    Windows Services Windows Services的操作
    Oracle下载及安装
  • 原文地址:https://www.cnblogs.com/upcwanghaibo/p/6522542.html
Copyright © 2011-2022 走看看