問題描述
來源:LeetCode第442題
難度:中等
給你一個長度為n的整數數組nums,其中nums的所有整數都在范圍[1,n]內,且每個整數出現一次或兩次。請你找出所有出現兩次的整數,并以數組形式返回。
你必須設計并實現一個時間復雜度為O(n)且僅使用常量額外空間的算法解決此問題。
示例 1:
輸入:nums = [4,3,2,7,8,2,3,1]
輸出:[2,3]
示例 2:
輸入:nums = [1,1,2]
輸出:[1]
示例 3:
輸入:nums = [1]
輸出:[]
提示:
n == nums.length
1 <= n <= 10^5
1 <= nums[i] <= n
nums 中的每個元素出現一次或兩次
問題分析
這題說的是找出重復的數字,本來看起來很簡單的一道題,但因為題中要求必須實現一個時間復雜度為O(n)且僅使用常量額外空間的算法解決此問題,所以通過排序或者是用集合Map首先被排除掉。
我們再來仔細看這道題,他說的數組nums中所有整數都在[1,n]內,我們可以把數組中的元素num放到第num個位置,因為數組的下標是從0開始的,也就是把元素num放到數組下標為num-1的位置。因為一個位置只能放一個元素,如果某個元素出現了兩次,那么肯定有一次是放在其他位置的。最后我們只需要找出元素和下標不匹配的即可,這里是示例一為例看個視頻
來看下代碼
public List<Integer> findDuplicates(int[] nums) {
int length = nums.length;
for (int i = 0; i < length; ++i) {
//把當前元素放到對應的位置
while (nums[i] != nums[nums[i] - 1]) {
swap(nums, i, nums[i] - 1);
}
}
List<Integer> res = new ArrayList<>();
// 如果當前元素的值和對應的下標不一致,說明出現了兩次
for (int i = 0; i < length; ++i) {
if (nums[i] - 1 != i) {
res.add(nums[i]);
}
}
return res;
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
其實還有一種方式,就是我們不交換,直接把對應位置的值變為負的,后面如果遇到對應位置為負的就說明出現了重復的。做視頻太累了,這里就不在弄了,直接畫個圖看下
看下代碼
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
int index = Math.abs(nums[i]) - 1;
// 如果當前位置的值是負數,說明出現過重復的
if (nums[index] < 0) {
res.add(index + 1);
continue;
}
nums[index] = -nums[index];
}
return res;
}