【LeetCode题解(C语言)】缺失的第一个正数

这里提供三种思路。

第一种解法,如果不管时间复杂度和内存,可以从1开始依次枚举正整数一直到数组中的最大值,并遍历数组,判断其是否在数组中。这个解法时间复杂度是O(N),有点高。

int firstMissingPositive(int* nums, int numsSize){
	int MaxN = 0;
	int i, j;
	for (int i = 0; i < numsSize; ++i)
		if (nums[i] > MaxN)
			MaxN = nums[i];
	
	for (i = 1; i <= MaxN; ++i){
		for (j = 0; j < numsSize; ++j)
			if (nums[j] == i) break;
		if (j == numsSize)
			return i;
	}
	return MaxN + 1;
}

第二种解法,既然这个数组未排序,那么就排序,再对数组进行处理。首先使用快速排序法从小到大进行排序,然后开始遍历数组中的每个数,如果第一个元素就大于或等于2,那么直接返回1,否则继续遍历,直到找到大于或等于2的整数,这时判断该值是否与前一值连续或相等(差值小于或等于1),如果连续或相等,则继续遍历,否则直接返回前一值+1。如果数组遍历完,函数仍未返回,有两种情况:第一种情况,该数组全部小于或等于1,则根据最后一个数判断返回1还是2,如果最后一个数小于等于0,则返回1,如果最后一个数等于1,则返回2;第二种情况,该数组有小于或等于1的部分也有大于1的部分,且大于1的部分与前一值一直保持连续或相等的状态,此时应返回数组的最后值+1。快速排序的时间复杂度是O(NlogN),遍历数组的时间复杂度是O(N),总体来说时间复杂度是O(NlogN)。

void quick_sort(int s[], int l, int r)
{
	if (l < r)
	{
		//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1  
		int i = l, j = r, x = s[l];
		while (i < j)
		{
			while (i < j && s[j] >= x) // 从右向左找第一个小于x的数  
				j--;
			if (i < j)
				s[i++] = s[j];

			while (i < j && s[i] < x) // 从左向右找第一个大于等于x的数  
				i++;
			if (i < j)
				s[j--] = s[i];
		}
		s[i] = x;
		quick_sort(s, l, i - 1); // 递归调用   
		quick_sort(s, i + 1, r);
	}
}

int firstMissingPositive(int* nums, int numsSize){
	int i, res;

	if (numsSize == 0)
		res = 1;
	else if (numsSize == 1){
		if (nums[0] <= 0) res = 1;
		else if (nums[0] == 1) res = 2;
		else res = 1;
	}
	else{
		quick_sort(nums, 0, numsSize-1);
		if (nums[0] <= 1){
			for (i = 1; i < numsSize; ++i){
				if (nums[i] >= 2)
					if (nums[i - 1] <= 0){
						res = 1;
						break;
					}
					else if (nums[i] - nums[i - 1] >1) {
						res = nums[i - 1] + 1;
						break;
					}
			}
			if (i == numsSize){
				if (nums[i - 1] <= 0) res = 1;
				else if (nums[i - 1] == 1) res = 2;
				else res = nums[i - 1] + 1;
			}
		}
		else res = 1;
	}
	return res;
}

第三种解法,官方解法的第一种解法,很给力。这里我就不把官方的原话搬过来了,感兴趣可以看官方教程https://leetcode-cn.com/problems/first-missing-positive/solution/que-shi-de-di-yi-ge-zheng-shu-by-leetcode-solution/。总结一下就是以下三步:

第一步,遍历数组,把每个小于等于0的数变成N+1,那么整个数组就变成了全为正整数的数组。

第二步,遍历新的数组,如果第i个元素的绝对值小于或等于N(这里取绝对值是因为该元素可能被改过成负数),则把对应的那个元素改成负数(如果是正数则取相反数,如果是负数那么不变),比如a[2]=4,C语言数组中第4个元素是a[3],此时如果a[3]=6,则把a[3]改成-6,如果a[3]=-6(证明之前修改过一次),则保持负数不变,当遍历到a[3]时,再把a[5]改成负数(abs(a[3])=6,对应a[5])。

前两步的目的相当于对原数组中出现在[1,N]区间的数x打上了“标记”,标记在第a[x-1]元素上,有“标记”的位置上的元素全部都是负数。未出现的正数,可以通过新数组中未被“标记”过的元素的位置来确定,如果新数组中某元素大于0,则说明未做“标记”,即原数组中没有出现过该正数,自然就不会在对应位置有“标记”。所以第三步,遍历第二步修改好的数组,找到第一个大于0的元素,该元素所在的位置索引+1即为原数组中缺失的第一个正数。

int firstMissingPositive(int* nums, int numsSize) {
    for (int i = 0; i < numsSize; ++i) {
        if (nums[i] <= 0) {
            nums[i] = numsSize + 1;
        }
    }
    for (int i = 0; i < numsSize; ++i) {
        int num = abs(nums[i]);
        if (num <= numsSize) {
            nums[num - 1] = -abs(nums[num - 1]);
        }
    }
    for (int i = 0; i < numsSize; ++i) {
        if (nums[i] > 0) {
            return i + 1;
        }
    }
    return numsSize + 1;
}

官方的解法很强,时间复杂度是O(n),但不清楚为什么内存消耗稍微比第二种方法多一点点。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值