Java 算法-Find the Duplicate Number(快慢指针)

  今天做了一道题,其实题的本身不难,就是因为题的要求,导致这个题的解法不能使用常规方法。先来看看题:

题意

Given an array nums containing n + 1 integers where each integer
 is between 1 and n (inclusive), prove that at least one duplicate 
number must exist. Assume that there is only one duplicate 
number, find the duplicate one.
注意事项:
1.You must not modify the array (assume the array is read only).
2.You must use only constant, O(1) extra space.
3.Your runtime complexity should be less than O(n^2).
4.There is only one duplicate number in the array, but it could be 5.repeated more than once.

样例

Given nums = [5,5,4,3,2,1] return 5
Given nums = [5,4,4,3,2,1] return 4

  其实这个题题的常规解法就是:两重循环,一个从头开始,一个从尾巴开始,如果有两个相同的数字,就跳出循环。但是题上的要求,是时间复杂度不能超过O(n^2),所以不能使用两重循环。

1.二分法

  才开始看到这个方法,有人不禁疑问,二分法不是用在有序的数组里面,而这里我们不能对数组进行排序。
  其实这里的二分法作用是:他不是在这个数组里面对重复的数字进行搜索,而是对重复的数字在[1,n]区间进行定位。意思就是说,默认开始时,重复数字肯定在[1,n]区间,然后我们统计这个数组里面小于等于mid = (1 + n) / 2的数据的个数,如果小于,那么重复数字一定在[mid + 1, n],反之(等于或者大于)在[1,mid]。
  为什么是这样的?想一想,数组一共有n + 1个,其中只能有一个数字被重复,那么这个数字至少被重复一次(可以被重复多次),这个数字如果在[1,mid]区间的话,这个数字重复的时候,肯定占据其他其他数字的位置,如果占据的是[1,mid]的数字的位置的话,那么最终个数等于或者大于mid,如果占据的是[mid + 1, n],最终个数小于mid。如图所示


Java 算法-Find the Duplicate Number(快慢指针)_第1张图片

(1).代码

public int findDuplicate(int[] nums) {
        int l = 1;
        int h = nums.length - 1;
        while (l < h) {
            int mid = l + (h - l) / 2;
            int count = 0;
            for (int i : nums) {
                if (i <= mid) {
                    count++;
                }
            }
            if (count <= mid)
                l = mid + 1;
            else
                h = mid;
        }
        return l;
    }

2.快慢指针

  首先,也是第一次的接触到快慢指针,简单的学习了一下,感觉非常的强大。
  首先,快慢指针表示的是,有两个指针,一个是快指针,每次向后移动两步;一个是慢指针,每次向后移动一步。而在单链表遍历中,快慢指针同时从头开始遍历,由于快指针速度较快,所以快指针肯定先到达终点。但是这里有一个特殊情况,如果单链表中有环怎么办?
  如果有环的话,快慢指针会再次的相遇,虽然快指针跑得快,但是有环,相当于是绕着一个环在跑,而慢指针也是绕着环再跑(只有当两个指针都进入了环才行,而且只要两个指针进入了环,就不能出环,如果一直遍历的话,会一直在环里面)。推导公式


Java 算法-Find the Duplicate Number(快慢指针)_第2张图片

  有了那么多的理论在前面,我们的代码就好理解了

(1).代码

public int findDuplicate(int[] nums) {
        int slow = 0;// 慢指针
        int slow1 = 0; // 慢指针
        int fast = 0; // 快指针
        while (true) {
            slow = nums[slow];// 慢指针前进一步
            fast = nums[nums[fast]];// 快指针前进两步

            if (slow == fast) { // 当相遇时退出
                break;
            }
        }
        while (true) {
            slow = nums[slow]; //原来的慢指针继续走
            slow1 = nums[slow1];//新的慢指针从头开始走
            if (slow1 == slow) { // 两个慢指针一定会在环的入口相遇,环的入口就是重复的数字
                break;
            }
        }
        return slow;
    }

你可能感兴趣的:(Java 算法-Find the Duplicate Number(快慢指针))