leetcode第41题

简介: 对于这种要求空间复杂度的,我们可以先考虑如果有一个等大的空间,我们可以怎么做。然后再考虑如果直接用原数组怎么做,主要是要保证数组的信息不要丢失。目前遇到的,主要有两种方法就是交换和取相反数。

image.png

给一串数字,找出缺失的最小正数。限制了时间复杂度为 O(n),空间复杂度为 O(1)。

解法一 交换

参考这里-space-and-O(n)-time?orderBy=most_votes)。

如果没限制空间复杂度,我们可以这样想。用一个等大的数组去顺序保存这些数字。

比如说,数组 nums [ 3 4 -1 1 8],它的大小是 5。然后再创建一个等大的数组 a,初始化为 [ - 1,- 1,- 1,- 1,-1] 。然后我们遍历 nums,把数字分别存到对应的位置。1 就存到数组 a 的第 1 个位置(a [ 0 ]),2 就存到数组 a 的第 2 个位置(a [ 1 ]),3 就存到数组 a 的第 3 个位置(a [ 2 ])...

nums [ 0 ] 等于 3,更新 a [ - 1,- 1,3,- 1,-1] 。

nums [ 1 ] 等于 4,更新 a [ - 1,- 1,3,4,-1 ] 。

nums [ 2 ] 等于 - 1,不是正数,忽略。

nums [ 3 ] 等于 1,更新 a [ 1,- 1,3,4,-1 ] 。

nums [ 4 ] 等于 8,我们的 a 数组只能存 1 到 5,所以同样忽略。

最后,我们只需要遍历 a 数组,遇到第一次 a [ i ] != i + 1,就说明缺失了 i + 1。因为我们的 a 数组每个位置都存着比下标大 1 的数。

当然,上边都是基于有一个额外空间讲的。如果没有额外空间,怎么办呢?

我们直接把原数组当成 a 数组去用。 这样的话,会出现的问题就是之前的数就会被覆盖掉。覆盖之前我们把它放回到当前数字的位置, 换句话说就是交换一下位置。然后把交换回来的数字放到应该在的位置,又交换回来的新数字继续判断,直到交换回来的数字小于 0,或者大于了数组的大小,或者它就是当前位置放的数字了。接着遍历 nums 的下一个数。具体看一下。

nums = [ 3 4 -1 1 8 ]

nums [ 0 ] 等于 3,把 3 放到第 3 个位置,并且把之前第 3 个位置的 -1 放回来,更新 nums [ -1, 4, 3, 1, 8 ]。

然后继续判断交换回来的数字,nums [ 0 ] 等于 -1,不是正数,忽略。

nums [ 1 ] 等于 4,把 4 放到第 4 个位置,并且把之前第 4个位置的 1 放回来,更新 nums [ -1, 1, 3, 4, 8 ]。

然后继续判断交换回来的数字,nums [ 1 ] 等于 1,把 1 放到第 1 个位置,并且把之前第 1 个位置的 -1 放回来,更新 nums [ 1-1, 3, 4, 8 ]。

然后继续判断交换回来的数字,nums [ 1 ] 等于 -1,不是正数,忽略。

nums [ 2 ] 等于 3,刚好在第 3 个位置,不用管。

nums [ 3 ] 等于 4,刚好在第 4 个位置,不用管。

nums [ 4 ] 等于 8,我们的 nums 数组只能存 1 到 5,所以同样忽略。

最后,我们只需要遍历 nums 数组,遇到第一次 nums [ i ] != i + 1,就说明缺失了 i + 1。因为我们的 nums 数组每个位置都存着比下标大 1 的数。

看下代码吧,一个 for 循环,里边再 while 循环

publicintfirstMissingPositive(int[] nums) {
intn=nums.length;
//遍历每个数字for (inti=0; i<n; i++) {
//判断交换回来的数字while (nums[i] >0&&nums[i] <=n&&nums[i] !=nums[nums[i] -1]) {
//第 nums[i] 个位置的下标是 nums[i] - 1swap(nums, i, nums[i] -1);
        }
    }
//找出第一个 nums[i] != i + 1 的位置for (inti=0; i<n; i++) {
if (nums[i] !=i+1) {
returni+1;
        }
    }
//如果之前的数都满足就返回 n + 1returnn+1;
}
privatevoidswap(int[] nums, inti, intj) {
inttemp=nums[i];
nums[i] =nums[j];
nums[j] =temp;
}

时间复杂度:for 循环里边套了个 while 循环,如果粗略的讲,那时间复杂度就是 O(n²)了。我们再从算法的逻辑上分析一下。因为每交换一次,就有一个数字放到了应该在的位置,只有 n 个数字,所以 while 里边的交换函数,最多执行 n 次。所以时间复杂度更精确的说,应该是 O(n)。

空间复杂度:O(1)。

对于这种要求空间复杂度的,我们可以先考虑如果有一个等大的空间,我们可以怎么做。然后再考虑如果直接用原数组怎么做,主要是要保证数组的信息不要丢失。目前遇到的,主要有两种方法就是交换和取相反数。

相关文章
|
9月前
|
C++ Python
leetcode-283:移动零
leetcode-283:移动零
32 0
|
9月前
|
算法
leetcode:389. 找不同
leetcode:389. 找不同
38 0
|
9月前
leetcode-475:供暖器
leetcode-475:供暖器
57 0
|
算法 C++ Python
每日算法系列【LeetCode 881】救生艇
每日算法系列【LeetCode 881】救生艇
|
Python
LeetCode 1904. 你完成的完整对局数
一款新的在线电子游戏在近期发布,在该电子游戏中,以 刻钟 为周期规划若干时长为 15 分钟 的游戏对局。这意味着,在 HH:00、HH:15、HH:30 和 HH:45 ,将会开始一个新的对局,其中 HH 用一个从 00 到 23 的整数表示。游戏中使用 24 小时制的时钟 ,所以一天中最早的时间是 00:00 ,最晚的时间是 23:59 。
110 0
|
算法
leetcode第47题
基本上都是在上道题的基础上改出来了,一些技巧也是经常遇到,比如先排序,然后判断和前一个是否重复。利用 Hash 去重的功能。利用原来的存储空间隐藏掉数据,然后再想办法还原。
100 0
leetcode第47题
|
存储 算法
leetcode第43题
个位乘个位,得出一个数,然后个位乘十位,全部乘完以后,就再用十位乘以各个位。然后百位乘以各个位,最后将每次得出的数相加。十位的结果要补 1 个 0 ,百位的结果要补两个 0 。相加的话我们可以直接用之前的大数相加。直接看代码吧。
100 0
leetcode第43题
leetcode第46题
这是自己开始想到的一个方法,考虑的思路是,先考虑小问题怎么解决,然后再利用小问题去解决大问题。没错,就是递归的思路。比如说, 如果只有 1 个数字 [ 1 ],那么很简单,直接返回 [ [ 1 ] ] 就 OK 了。 如果加了 1 个数字 2, [ 1 2 ] 该怎么办呢?我们只需要在上边的情况里,在 1 的空隙,也就是左边右边插入 2 就够了。变成 [ [ 2 1 ], [ 1 2 ] ]。
leetcode第46题
leetcode第36题
一个 9 * 9 的数独的棋盘。判断已经写入数字的棋盘是不是合法。需要满足下边三点, • 每一行的数字不能重复 • 每一列的数字不能重复 • 9 个 3 * 3 的小棋盘中的数字也不能重复。 只能是 1 - 9 中的数字,不需要考虑数独最后能不能填满
102 0
leetcode第36题
|
算法
leetcode第30题
如上图,利用循环变量 i ,依次后移,判断每个子串是否符合即可。 怎么判断子串是否符合?这也是这个题的难点了,由于子串包含的单词顺序并不需要固定,如果是两个单词 A,B,我们只需要判断子串是否是 AB 或者 BA 即可。如果是三个单词 A,B,C 也还好,只需要判断子串是否是 ABC,或者 ACB,BAC,BCA,CAB,CBA 就可以了,但如果更多单词呢?那就崩溃了。 链接)的作者提出了,用两个 HashMap 来解决。首先,我们把所有的单词存到 HashMap 里,key 直接存单词,value 存单词出现的个数(因为给出的单词可能会有重复的,所以可能是 1 或 2 或者其他)。然后扫描子
132 0
leetcode第30题