(leetcode)力扣100 17缺失的第一个正数(哈希)

题目

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

数据范围

1 <= nums.length <= 105

-231 <= nums[i] <= 231 - 1

测试样例

示例1

java 复制代码
输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例2

java 复制代码
输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例3

java 复制代码
输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

题解(哈希)

java 复制代码
public static int firstMissingPositive(int[] nums) {
        int len= nums.length;
        for(int i=0;i<len;i++){
            if(nums[i]<=0){
                nums[i]=len+1;
            }
        }

        for(int i=0;i<len;i++){
            int r=Math.abs(nums[i]);
            if(r>=1&&r<=len&&nums[r-1]>0){
                nums[r-1]*=-1;
            }
        }

        for(int i=0;i<len;i++){
            if(nums[i]>0){
                return i+1;
            }
        }

        return len+1;
    }

思路

这一道题,从各个方面来说,博主认为难点并不在于其逻辑过程,而是有没有这种思路。反正博主在这之前从来没有做过为了节约空间使用题目给的数组,从而达到常数级的空间复杂度。在我看来这是掩耳盗铃,因为你既然用了题目给的数组,空间复杂度已经到了O(n),除开这个数组算空间复杂度,有一种为了出题而出题,高中的那些恶心人的数学题的感觉。

说回题目,怎么达到常数级的空间复杂度,理论上不可能,但如果你默认你默认,题目给你的数组不算空间复杂度,然后基于这一点,使用这个数组(想到这里就难崩),就可以实现。

当然这道题也不仅限于逻辑奇怪,它还是提供了两个比较优秀的思路。

第一点,我们还要想通一个逻辑,N个数,除开这些的最小正整数一定是1-n+1,我们不妨假设N个数刚好就是1-n,那么最小正整数就是n+1,如果N个数有一个数不在1-n,1-n的位置就会被空出相应个数,我们的答案也同样在这里面。

第二点,原数组已经有数了,怎么在此基础上用哈希?这一点非常漂亮,使用了正负号作为哈希标记。

大概细节就这些,具体逻辑我引用官方题解的部分文字,就不重复描述了,毕竟博主也是看的题解,不是自己思路不想描述太多。

仔细想一想,我们为什么要使用哈希表?这是因为哈希表是一个可以支持快速查找的数据结构:给定一个元素,我们可以在 O(1) 的时间查找该元素是否在哈希表中。因此,我们可以考虑将给定的数组设计成哈希表的「替代产品」。

实际上,对于一个长度为 N 的数组,其中没有出现的最小正整数只能在 [1,N+1] 中。这是因为如果 [1,N] 都出现了,那么答案是

N+1,否则答案是 [1,N] 中没有出现的最小正整数。这样一来,我们将所有在 [1,N]

范围内的数放入哈希表,也可以得到最终的答案。而给定的数组恰好长度为 N,这让我们有了一种将数组设计成哈希表的思路:

我们对数组进行遍历,对于遍历到的数 x,如果它在 [1,N] 的范围内,那么就将数组中的第 x−1 个位置(注意:数组下标从 0

开始)打上「标记」。在遍历结束之后,如果所有的位置都被打上了标记,那么答案是 N+1,否则答案是最小的没有打上标记的位置加 1。

那么如何设计这个「标记」呢?由于数组中的数没有任何限制,因此这并不是一件容易的事情。但我们可以继续利用上面的提到的性质:由于我们只在意

1,N\] 中的数,因此我们可以先对数组进行遍历,把不在 \[1,N\] 范围内的数修改成任意一个大于 N 的数(例如 N+1)。这样一来,数组中的所有数就都是正数了,因此我们就可以将「标记」表示为「负号」。算法的流程如下: 我们将数组中所有小于等于 0 的数修改为 N+1; 我们遍历数组中的每一个数 x,它可能已经被打了标记,因此原本对应的数为 ∣x∣,其中 ∣∣ 为绝对值符号。如果 ∣x∣∈\[1,N\],那么我们给数组中的第 ∣x∣−1 个位置的数添加一个负号。注意如果它已经有负号,不需要重复添加; 在遍历完成之后,如果数组中的每一个数都是负数,那么答案是 N+1,否则答案是第一个正数的位置加 1。 作者:力扣官方题解 链接:https://leetcode.cn/problems/first-missing-positive/solutions/304743/que-shi-de-di-yi-ge-zheng-shu-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐
山上三树几秒前
详细介绍 C 语言 typedef 及与 #define 的核心对比
c语言·数据结构·算法
释怀°Believe6 分钟前
Daily算法刷题【面试经典150题-7️⃣位运算/数学/】
算法·面试·职场和发展
2401_8762213416 分钟前
因数个数、因数和、因数积
c++·算法
云里雾里!26 分钟前
LeetCode 744. 寻找比目标字母大的最小字母 | 从低效到最优的二分解法优化
算法·leetcode
一条大祥脚41 分钟前
26.1.3 快速幂+容斥 树上dp+快速幂 带前缀和的快速幂 正序转倒序 子序列自动机 线段树维护滑窗
数据结构·算法
二狗哈1 小时前
czsc入门5: Tick RawBar(原始k线) NewBar (新K线)
算法·czsc
꧁Q༒ོγ꧂1 小时前
算法详解(四)--排序与离散化
数据结构·算法·排序算法
Tisfy1 小时前
LeetCode 0865.具有所有最深节点的最小子树:深度优先搜索(一次DFS + Python5行)
算法·leetcode·深度优先·dfs·题解
Q741_1471 小时前
C++ 队列 宽度优先搜索 BFS 力扣 429. N 叉树的层序遍历 C++ 每日一题
c++·算法·leetcode·bfs·宽度优先
Yzzz-F1 小时前
P4145 上帝造题的七分钟 2 / 花神游历各国[线段树 区间开方(剪枝) + 区间求和]
算法·机器学习·剪枝