这种原地哈希算法适用于和正整数有关,且数字范围和数组长度有关的题目里,映射之后能利用映射关系(下标和值一一对应)来找到解。
1.题目链接:https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/
- 由于不能使用额外空间,哈希表等其他数据结构就无法使用了。
- 我们采取一种特殊的原地哈希:
f(nums[i]) = num[i] - 1
,也就是将值为nums[i]的数字映射到nums[i]-1的下标位置,例:3映射到数组下标为2。 - 算法过程:
- 1.遍历数组,然后采取原地哈希,将现在位置上的数和映射过后下标(idx)上的数交换,循环直到
nums[nums[i] - 1] != nums[i]
(也就是idx上的数和目前的数一样了)。 - 2.最后再遍历一下这个调整过的数组,发现值和下标不对应的话,说明和这个下标对应的值没有出现过了。
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int n = nums.size();
auto ans = vector<int>{};
//f(nums[i]) = nums[i] - 1
for(int i = 0; i < n; ++i){
while(nums[nums[i] - 1] != nums[i]){
int idx = nums[i] - 1;
int t = nums[idx];
nums[idx] = nums[i];
nums[i] = t;
}
}
for(int i = 0; i < n; ++i){
if(nums[i] - 1 != i) ans.push_back(i + 1);
}
return ans;
}
};
2.https://leetcode-cn.com/problems/first-missing-positive/
- 思路和上题差不多也是采用原地哈希。
- 将小于n且大于0的值映射到相关位置上(
index = nums[i] - 1
),这样重新遍历的时候,第一个和下标不对应的就是第一个没出现过的最小的正整数。 - 这里映射的时候要减1,不能2映射到下标2,不然的话下标0放什么数就不懂了。
class Solution {
public:
int firstMissingPositive(vector<int> &nums) {
int n = nums.size();
for (int i = 0; i < n; i++) {
while (nums[i] != i + 1) {
if (nums[i] <= 0 || nums[i] > n || nums[i] == nums[nums[i] - 1])
break;
// 将nums[i] 放置到对应位置上[1,2,3...]
int idx = nums[i] - 1;
nums[i] = nums[idx];
nums[idx] = idx + 1;
}
}
for (int i = 0; i < n; i++) {
if (nums[i] != (i + 1)) {
return (i + 1);
}
}
return (n + 1);
}
};
3.https://leetcode-cn.com/problems/find-all-duplicates-in-an-array/
- 同样原地哈希。
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
auto ans = vector<int>{};
for(int i = 0; i < n; ++i){
while(nums[nums[i] - 1] != nums[i]){
int idx = nums[i] - 1;
int t = nums[idx];
nums[idx] = nums[i];
nums[i] = t;
}
}
for(int i = 0; i < n; ++i){
if(i != nums[i] - 1) ans.push_back(nums[i]);
}
return ans;
// unordered_set<int> hash_set;
// for(auto num : nums){
// if(hash_set.count(num)) ans.push_back(num);
// else hash_set.insert(num);
// }
// return ans;
}
};