目录
题目描述
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
解法一:抽屉原理
思路一
仔细观察题目描述 1 ≤ a[i] ≤ n (n为数组长度),数组里的不同的元素类别可视为抽屉。数组个数视之为苹果
1.从头到位遍历数组如果发现 i==(a[i]-1),说明a[i]处的数据是对的,直接跳过。
2. i!=(a[i]-1)的话再判断a[a[i]-1]==a[i]是否成立,成立的话说明a[i]出现过2次,直接放入结果
3.a[a[i]-1]!=a[i]的话交换下标a[i]-1和i处的数据,交换之后可能导致a[i]-1>i,这时i可以不用继续向前移动,a[i]-1<=i的话要继续移动i
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> ans = new ArrayList<Integer>();
int n = nums.length;
for( int i =0;i<n;i++ ){
//索引 和 (数-1) 对应了
if( nums[i]-1==i ){
continue;
}
int curVal = nums[i];
// 没有对应的时候
// 如果当前值curVal 所对应的索引curVal-1处的值nums[curVal-1]相等,说明这个值出现了两次
if( nums[curVal-1] == curVal ){
ans.add(curVal);
}else{
//如果不对应,交换两处的值
nums[i] = nums[curVal-1] ;
nums[curVal-1] = curVal;
//由于是向前遍历,当 i > curVal-1 时,说明curVal-1索引处的值被处理过,这里直接交换
// 当i<curVal-1时,curVal-1索引处的值没有处理过,先交换
// 交换之后继续对当前值处理,由于循环会i++;因此这里i--;使得下一次循环依然在原地处理
if( i<curVal-1 ){
i--;
}
}
}
return ans;
}
}
思路二
把下标index和下标nums[index]处的数位互换,数组重复或则之前已经有序的下标结束循环
class Solution {
public int findDuplicate(int[] nums) {
int index = 0;
while(index < nums.length){
if(nums[index]-1!= index){
//这种情况构成循环所以要额外判断
if(nums[index] == nums[nums[index]-1]){
return nums[index];
}
//index之前的下标已经有序,再次出现小于的元素可认为是重复
if(nums[index]-1 < index){
return nums[index];
}
swap(nums,nums[index]-1,index);
}else {
index++;
}
}
return 0;
}
private void swap(int[] nums,int i,int j){
int tmep = nums[i];
nums[i] = nums[j];
nums[j] = tmep;
}
}
解法二:位运算
[1,n]的二进制中的各位是1的数次是固定的,比如
输入:nums = [1, 3, 4, 2, 2] 输出:2
[1, 4]的位数如下
从上面我们可以知道,如果第i位1的总数x > y,那么重复数的第i位就是1;
class Solution {
public int findDuplicate(int[] nums) {
int n = nums.length, ans = 0;
int bit_max = 31;
while (((n - 1) >> bit_max) == 0) {
bit_max -= 1;
}
for (int bit = 0; bit <= bit_max; ++bit) {
int x = 0, y = 0;
for (int i = 0; i < n; ++i) {
//把nums[1]-nums[n]的第bit位为1的挨个计算一遍
if ((nums[i] & (1 << bit)) != 0) {
x += 1;
}
//把1-n的第bit位为1的挨个计算一遍
if (i >= 1 && ((i & (1 << bit)) != 0)) {
y += 1;
}
}
if (x > y) {
ans |= 1 << bit;
}
}
return ans;
}
}
解题总结
掌握解题的思想之后代码只是实现思想的工具,前期需要大量储备各种思想
另外本题还有二分和快慢指针的求解方法
LeetCode287之寻找重复数(相关话题:二分查找,快慢指针)_leetcode寻找重复记录_数据与后端架构提升之路的博客-CSDN博客