287. Find the Duplicate Number--binary search/快慢指针

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.

Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).

There is only one duplicate number in the array, but it could be repeated more than once.


1.O(nlgn)的解法:

思想和之前的378. Kth Smallest Element in a Sorted Matrix-binary search/heap相似。

题目要求我们不能改变原数组,即不能给原数组排序,又不能用多余空间,那么哈希表神马的也就不用考虑了,又说时间小于O(n2),也就不能用brute force的方法,那我们也就只能考虑用二分搜索法了,我们在区别[1, n]中搜索,首先求出中点mid,然后遍历整个数组,统计所有小于等于mid的数的个数,如果个数大于mid,则说明重复值在[mid+1, n]之间,反之,重复值应在[1, mid-1]之间,然后依次类推,直到搜索完成,此时的low就是我们要求的重复值,参见代码如下:

int findDuplicate(int* nums, int numsSize) {
    int i = 1, j = numsSize - 1, mid, cnt;
    while(i < j){
        cnt = 0;
        mid = (i + j)/2;
        for(int k = 0; k < numsSize; k++)
            if(nums[k] <= mid) cnt++;
        if(cnt > mid) j = mid; 
        else i = mid + 1;
    }
    return i;
}

2.O(n)的解法:

思想与Linked list cyclelinked list cycle 2相似。

这题还有O(n)的解法,不过没看懂。。。其核心思想快慢指针在之前的题目Linked List Cycle II中就有应用,这里应用的更加巧妙一些。参见: https://discuss.leetcode.com/topic/25913/my-easy-understood-solution-with-o-n-time-and-o-1-space-without-modifying-the-array-with-clear-explanation


看到这题可能会想到之前遇到的一类问题singleNumber

1)数组中除一个元素外其他所有元素出现二次,找到只出现一次的那个元素。

2)数组中除一个元素外其他所有元素出现三次,找到只出现一次的那个元素。

3)数组中所有元素出现两次,其中有两个元素只出现一次,找出这两个元素。

........

因而,就可能想到使用异或的方法,但是并没有找到异或的方法。。


3.联想:

既然提到了上面的三个问题,那就总结下这三个问题的思路吧:

参见:http://www.cnblogs.com/grandyang/p/4741122.html 

http://www.cnblogs.com/grandyang/p/4263927.html

1)所有项都异或,相同的项异或结果为0.那么剩下的值就是那个单项的值了。

public int singleNumber(int[] nums) {
        int res = nums[0];
        for (int i = 1; i < nums.length; i++)
            res = res ^ nums[i];

        return res;
    }

2)这道题是之前那道 Single Number 单独的数字 的延伸,那道题的解法就比较独特,是利用计算机按位储存数字的特性来做的,这道题就是除了一个单独的数字之外,数组中其他的数字都出现了三次,那么还是要利用位操作 Bit Operation 来解此题。我们可以建立一个32位的数字,来统计每一位上1出现的个数,我们知道如果某一位上为1的话,那么如果该整数出现了三次,对3去余为0,我们把每个数的对应位都加起来对3取余,最终剩下来的那个数就是单独的数字。代码如下:

public static int singleNumber(int[] nums) {
    int len = nums.length, result = 0;
    for (int i = 0; i < 32; i++) {
        int sum = 0;
        for (int j = 0; j < len; j++) {
            sum += (nums[j] >> i) & 1;
        }
        result |= (sum % 3) << i;
    }
    return result;
    }
3) 这道题是之前那两道Single Number 单独的数字和 Single Number II 单独的数字之二的再次延伸,说实话,这类位操作Bit Manipulation的题,如果之前没有遇到过类似的题目,楞想是很难相出来的,于是我只能上网搜大神们的解法,发现还真是巧妙啊。这道题其实是很巧妙的利用了Single Number 单独的数字的解法,因为那道解法是可以准确的找出只出现了一次的数字,但前提是其他数字必须出现两次才行。而这题有两个数字都只出现了一次,那么我们如果能想办法把原数组分为两个小数组,不相同的两个数字分别在两个小数组中,这样分别调用Single Number 单独的数字的解法就可以得到答案。那么如何实现呢,首先我们先把原数组全部异或起来,那么我们会得到一个数字,这个数字是两个不相同的数字异或的结果,我们取出其中任意一位为‘1’的位,为了方便起见,我们用 a &= -a 来取出最右端为‘1’的位,然后和原数组中的数字挨个相与,那么我们要求的两个不同的数字就被分到了两个小组中,分别将两个小组中的数字都异或起来,就可以得到最终结果了,参见代码如下:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int diff = accumulate(nums.begin(), nums.end(), 0, bit_xor<int>());
        diff &= -diff;
        vector<int> res(2, 0);
        for (auto &a : nums) {
            if (a & diff) res[0] ^= a;
            else res[1] ^= a;
        }
        return res;
    }
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值