前言
hash赋能可以为后面的工作大大减少搜索的时间,可用hash数组、hashSet、hashMap、原地数组hash。
一、缺失的第一个正数
二、hash赋能
1、hashSet
//hash赋能
//Time:O(n),Space:O(n)
public int firstMissingPositive(int[] nums) {
//hash赋能,记录访问过的正整数,以便正整数遍历时可以O(1)时间搜索对应的正整数是否存在过。
Set<Integer> hash = new HashSet<>();
for (int num : nums) hash.add(num);
int len = nums.length;
for (int i = 1; i <= len; i++) if (!hash.contains(i)) return i;
return len + 1;
}
2、原地数组hash
//根据要求,Space:O(1),但又不得不hash记录,那就只有用该数组来作为数组hash。
//如何把该数组作为数组hash,可以采用置换的方式。
//Time:O(n);Space:O(1)
public int firstMissingPositive2(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; ) {
//条件1:数字必须是在len之内的正整数;条件2:该数字不能已经在位置上;条件3:以前的位置已经hash完成,如果换完就会形成死循环。
if (nums[i] > 0 && nums[i] <= len && i != nums[i] - 1 && nums[nums[i] - 1] != nums[i]) {
//hash化,数值置换。
int t = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = t;
} else ++i;//该位置不用hash,直接往后走。
}
//循环判断每个位置是否为hash值,不是则为缺失的那个正整数。
for (int i = 0; i < len; i++) if (i != nums[i] - 1) return i + 1;
//所有hash完成,应该返回i + 1,此时的i == len,越界break循环了。return len + 1;
return len + 1;
}
总结
1)hash赋能,尤其注意隐式hash赋能–原地数组hash。