18.<tag-数组和原地哈希>-lt.41-缺失的第一个正数 + 剑指 Offer 03- 数组中重复的数字 1.1
什么是原地哈希? 就是在数组中建立像哈希表的那种映射关系(key-value 等价于 index-nums[index] ),
通过使用数组代替哈希表, 能够大大减少空间复杂度, 具体是怎么样的映射关系, 还是要根据题目要求自己设定;
缺点就是容易混乱, nums[nums[i]] 这样式儿的, 还是那句话, 多在草纸上写写画画!
lt.41-缺失的第一个正数
[案例需求]
[思路分析]
- 仔细第一遍这个题目, 就会发现这题的目的无非就是验证数组中是否包含1,2,3,4,5,…N(N为数组的长度), 因为要求是不在数组的最小数, 所以返回的就是1~nums.length这些数中, 不在数组中的数罢了.
- 最容易想到的方法就是哈希法, 把nums中的数全部放入到集合中, 然后遍历1~nums.length, 找到1~nums.length不存于哈希表的第一个数, 便于缺失的最小数.
[代码实现]
class Solution {
public int firstMissingPositive(int[] nums) {
//哈希表法
Set<Integer> set = new HashSet<>();
int len = nums.length;
//把nums的数全部放入到set中, set会对这些数, 排序和去重
//省心呀.
for(int i = 0; i < len; i++){
set.add(nums[i]);
}
//这样我们只需要比较一下在 1-len的范围内, 哪些数不在set中了.(返回第一个数即可)
for(int i = 1; i <= len; i++){
if(!set.contains(i)return i;
}
//如果1-len都在set内, 返回的是len + 1;
return len + 1;
}
}
比较容易错的一点就是: len + 1;
这意味着 1-len的数都在nums中, 所以我们缺失的最小正数应该是len + 1;
时间复杂度: 这里 N 表示数组的长度。第 1 次遍历了数组,第 2 次遍历了区间 [1, len] 里的元素。
空间复杂度: 一个哈希表HashSet, O(n)
[思路分析二, 把数组当做是哈希表]
- 由于题目要求我们「只能使用常数级别的空间」,而要找的数一定在 [1, N + 1] 左闭右闭(这里 N 是数组的长度)这个区间里。因此,我们可以就把原始的数组当做哈希表来使用。事实上,哈希表其实本身也是一个数组;
- 我们要找的数就在
[1, N + 1]
里,最后 N + 1 这个元素我们不用找。因为在前面的 N 个元素都找不到的情况下,我们才返回 N + 1; - 那么,我们可以采取这样的思路:就把 1 这个数放到下标为 0 的位置, 2 这个数放到下标为 1 的位置,按照这种思路整理一遍数组。然后我们再遍历一次数组,第 1 个遇到的它的
值不等于下标的那个数
,就是我们要找的缺失的第一个正数。 - 这个思想就相当于我们自己编写哈希函数,这个哈希函数的规则特别简单,那就是数值为 i 的数映射到下标为 i - 1 的位置
题解动画: 点我
// 原地哈希(把数组当做哈希表来用)解法
class Solution {
public int firstMissingPositive(int[] nums) {
/**
遍历数组, 把数值为i的元素放置在索引为i-1的位置上,
再遍历一次数组, 如果当前索引index + 1 不等于 nums[index] 就说明是第一个缺失的整数, 为 index + 1
*/
int len = nums.length;
for(int i = 0; i < len; i++){
while(nums[i] >= 1 && nums[i] <= len && nums[i] != nums[nums[i] - 1]){
swap(nums, i, nums[i] - 1);
}
}
//再次遍历, 找出第一个索引值+1 != nums[i]的数, 找不到就返回数组的长度+1
for(int i = 0; i < len; i++){
if(nums[i] != i + 1){
return i + 1;
}
}
return len + 1;
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
剑指 Offer 03- 数组中重复的数字
[案例需求]
[思路分析]
- 三种解法务必都要熟练掌握, 最重要的是第三种原地哈希法, 利用数组来的index和nums[index]建立起来像哈希表的那种映射关系
- 空间复杂度大大的减小, 好用!
[代码实现一, 哈希表]
class Solution {
public int findRepeatNumber(int[] nums) {
//1. HashSet
Set<Integer> hashSet = new HashSet<>();
for(int i = 0; i < nums.length; i++){
if(hashSet.contains(nums[i])) return nums[i];
hashSet.add(nums[i]);
}
return -1;
}
}
[代码实现二, 排序后遍历查找]
class Solution {
public int findRepeatNumber(int[] nums) {
//2. 普通解法
Arrays.sort(nums);
for(int i = 0, len = nums.length; i < len; i++){
if(i < len - 1 && nums[i] == nums[i+1]){
return nums[i];
}
}
return -1;
}
}
[代码实现三, 原地哈希法]
//3. 原地哈希
class Solution {
public int findRepeatNumber(int[] nums) {
//3. 原地交换!!// 原地哈希
// 用数组模仿哈希表的映射方式 (i, nums[i])
int len = nums.length;
for(int i = 0; i < len; i++){
while(nums[i] != i){
if(nums[nums[i]] == nums[i]) return nums[i];
swap(nums, i, nums[i]);
}
}
return -1;
}
public void swap(int[] nums, int left, int right){
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战