First Missing Positive
Posted april01xxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了First Missing Positive相关的知识,希望对你有一定的参考价值。
1.题目
Given an unsorted integer array, find the smallest missing positive integer.
*Example 1:*
Input: [1, 2, 0]
Output: 3
*Example 2:*
Input: [3, 4, -1, 1]
Output: 2
Note:
Your algorithm should run in O(n) time and uses constant extra space.
2.思路
这个题目自己想了好久也没找到一种常量空间开销的O(n)解法,总想着用额外的数组去记录每个元素
出现的情况,还想着万一有的元素特别大,岂不是要开巨大的空间?而实际上是完全不必要的,之所以会这么
想,还是对题意理解不够深.
给定一个无序的数组,找到第一个丢失的正整数.显然,若数组中没有1,那么答案是1,若有1,接着判断是否有2,若有2,接着判断是否有3...如此循环下去.
自己想到这里就没有深入的思考了,觉得要开一个无限大的数组来记录每个正整数出现的情况,还总想着找
到最小的连续的区间,这样就能得到题目的解.整个思维陷入误区.其实上面的思路顺着想下去就会发现,假
定数组有n个元素,那么最多循环n次判断就结束了,这就表示只需要开一个与原数组等大小的空间来记录
1~n这n个正整数出现的情况.
但题目要求空间开销是常量,怎么办?首先必须确定一点:必须要有地方记录1~n区间内正整数出现的情况.
既然不允许额外的空间,那就只能使用原数组的空间了.如何使用呢?这也是这个算法最精妙的地方:
将1~n区间内出现的正整数移动到它该在的地方去.举例来说:如果1
出现了,那就把它移动到数组中下标为0的地方,2出现了,就移动到下标为1的地方,若元素小于等于0或
者目标点的元素已经等于要移动的元素,则不用管.但这个过程中对于某一个元素可能会出现多次移动的
情况,例如对于上述第二个例子:[3, 4, -1, 1],第一次移动后:[-1,4,3,1],第二次移动后:
[-1, 1, 3, 4],此时元素1并不在它该在的地方(下标0处),那么还需要继续移动,最终得到:
[1, -1, 3, 4].
整个算法描述清楚了,现在唯一的担心是,上述这样的交换元素与数组的排序过程很像,而时间复杂度
非常低的快排的也要O(nlogn),超出了题目要求的O(n)限定.我们的算法真的是O(n)吗?
我们来简略的分析下,考虑最坏的情况,数组由1~n这n个正整数组成(若数组中有大于n的元素出现,那么交换的次数肯定比1~n的情况少),什么时候下会出现交换次数最多的情况呢?我们来回顾下我们的算法:第一次交换完成后,两个元素中有一个在正确的位置,剩下一个元素可能不在正确的位置,它可能需要一次交换,那么第二次交换后,两个元素都在正确的位置了,但是第二次交换中移动的那个元素可能由不在正确的位置了,需要额外的交换.这样看来每次都可能需要n次交换,算法的复杂度是n^2
.但是情况真的是这样吗?显然不是的,从整体来考虑,假定n个元素完全无序,但你知道1应该被放到下标0处,2应放到下标1处,即:array[array[i] - 1] = array[i]
.所以每一次交换一定会将一个元素放到正确位置,那么最多只需要n次即可将所有元素放到正确位置.体现在算法的每一步那就是:若某一步需要非常多的交换,那么后续的步骤需要交换的次数将会减少(有点类似摊还).所以算法的时间复杂度是O(n).
3.实现
int
firstMissingPositive(int *nums, int numsSize) {
int i, temp;
for (i = 0; i < numsSize; ++i) {
/**
* 若发现某个元素小于等于数组大小,则将其放到正确位置.注意避免不必要的交换.
* 例如数组[3,4,-1,1]:
* 1. [-1,4,3,1]
* 2. [-1,1,3,4],此时发现nums[1] = 1,而1应该被放在下标0处,故继续交换直到
* nums[i] == nums[nums[i] - 1]为止.
*/
while (nums[i] > 0 && nums[i] <= numsSize && nums[i] != nums[nums[i] - 1]) {
temp = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = temp;
}
}
/**
* 到此为止,元素都被放在了正确的位置,例如:
* 1被放在下标0处,2被放在下标1处...
* 遍历数组若发现nums[i] != i + 1,则找到了第一个缺失的正整数.
*/
for (i = 0; i < numsSize && nums[i] == i + 1; ++i)
;
return i + 1;
}
以上是关于First Missing Positive的主要内容,如果未能解决你的问题,请参考以下文章
Leetcode 41. First Missing Positive
41. First Missing Positive *HARD*
leetCode题解之First Missing Positive