码迷,mamicode.com
首页 > 其他好文 > 详细

First Missing Positive

时间:2018-07-28 22:23:09      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:color   input   需要   ret   应该   不用   结束   algorithm   first   

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

标签:color   input   需要   ret   应该   不用   结束   algorithm   first   

原文地址:https://www.cnblogs.com/April01xxx/p/9383645.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!