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

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

       (注:这里所说的指针,并不是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;
    }

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


        




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值