
1.本题首先想到的方法便是哈希表,但本题要求空间复杂度为O(1),实在想不出别的方法,所以选择去理解答案。
2.第一种答案的方法本质上也是哈希表思想,只不过是利用原数组来存储信息,利用负号来做标记,它的完整思路如下:
(1)对于一个大小为N的数组,缺失的第一个正数一定在1, N + 1范围内,当数组中的元素为1, N时答案才会为N + 1。该数组中会出现一些我们不需要关心的垃圾值,即非正数以及大于N的数,所以第一步应该是清除垃圾值。遍历一遍数组,将这些值全部变为N + 1(或其他不需要关心的值)。
cpp
1. for (int i = 0; i < numsSize; i++){
2. if (nums[i] <= 0) nums[i] = numsSize + 1;
3. }
(2)之后就开始进行标记,再次遍历数组,当遍历到的元素为需要关心的值时,将该元素值-1的位置标记为负数。需要注意,每次取值的时候要取绝对值num = abs(numsi),避免该位置已经被标记过负数;且在将numsnum - 1标记为负数时也不要忘记先取绝对值再进行标记。
cpp
1. for (int i = 0; i < numsSize; i++){
2. int num = abs(nums[i]);
3. if (num <= numsSize) nums[num - 1] = -abs(nums[num - 1]);
4. }
(3)最后再遍历一遍数组,当遍历到第一个正数时,取它的下标+1即为答案。若遍历一遍后都没有得出答案,则说明遇到了特殊情况,答案即为N + 1。
cpp
1. for (int i = 0; i < numsSize; i++){
2. if (nums[i] > 0) return i + 1;
3. }
3.基于以上思想,可写出完整代码如下:
cpp
1. // 寻找缺失的第一个正整数,原地数组标记法
2. int firstMissingPositive(int* nums, int numsSize) {
3. // 将所有非正数修改为numsSize+1,不参与后续标记
4. for (int i = 0; i < numsSize; i++){
5. if (nums[i] <= 0) nums[i] = numsSize + 1;
6. }
7. // 取绝对值得到数字,在对应下标位置标记负数,表示该数字存在
8. for (int i = 0; i < numsSize; i++){
9. int num = abs(nums[i]);
10. if (num <= numsSize) nums[num - 1] = -abs(nums[num - 1]);
11. }
12. // 遍历数组,第一个正数对应的下标+1就是缺失的最小正整数
13. for (int i = 0; i < numsSize; i++){
14. if (nums[i] > 0) return i + 1;
15. }
16. // 1~numsSize全部存在,缺失的是numsSize+1
17. return numsSize + 1;
18. }
该算法时间复杂度为O(n),空间复杂度为O(1)。
4.第二种方法是置换法。将每个需要关心的值都送到它对应值下标-1的位置,最后再遍历一遍数组,发现哪里位置不匹配,则下标+1即为答案。完整代码如下:
cpp
1. int firstMissingPositive(int* nums, int numsSize) {
2. for (int i = 0; i < numsSize; i++){
3. while (nums[i] > 0 && nums[i] <= numsSize && nums[i] != nums[nums[i] - 1]){
4. int tmp = nums[nums[i] - 1];
5. nums[nums[i] - 1] = nums[i];
6. nums[i] = tmp;
7. }
8. }
9.
10. for (int i = 0; i < numsSize; i++){
11. if (nums[i] != i + 1) return i + 1;
12. }
13.
14. return numsSize + 1;
15. }
该算法时间复杂度为O(n),空间复杂度为O(1)。
5.该方法需要注意两点:
(1)第一次for循环中换位判断需要使用while,因为换来的数仍可能是需要关心的值。且为了避免出现需要交换的两数数值相同导致出现死循环,需要额外加上numsi != numsnums\[i - 1]的条件,将重复的那个数也当成垃圾值处理。
(2)循环交换代码只能按照给出的代码中那样的顺序写,不能写成如下形式:
cpp
1. int tmp = nums[i];
2. nums[i] = nums[nums[i] - 1];
3. nums[nums[i] - 1] = tmp;
因为numsi的值不仅是待交换值,还会参与到后面numsnums\[i - 1]的计算中。如果先把numsi的值进行了修改,就会导致numsnums\[i - 1]的numsi -- 1下标出现变化导致错误!