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 cycle、linked 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 singleNumber(vector& nums) {
int diff = accumulate(nums.begin(), nums.end(), 0, bit_xor());
diff &= -diff;
vector res(2, 0);
for (auto &a : nums) {
if (a & diff) res[0] ^= a;
else res[1] ^= a;
}
return res;
}
};