待字闺中: 缺失的数字

原题

给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。

分析

首先数组是无序的,找到第一个大于0且不在数组中的元素,就是要找到大于0且不在数组中的最小的整数。要怎么处理呢?要找到最小的,不妨尝试从小到 大排序,然后从1开始,查找是否在数组中,可以利用二分查找。这样整体的时间复杂度是O(nlogn),空间复杂度是O(1)。

离题目的要求,还差一些。如何改进呢?针对排序好的数组,我们做如下的观察:

  • 当缺失的数字为1的时候,A[0]<=0, A[1] != 1

  • 当缺失的数字为2的时候,A[0]<=0, A[1] = 1, A[2] != 2

  • 当缺失的数字为3的时候,A[0]<=0, A[1] = 1, A[2] = 2, A[3] != 3

通过上面的观察,可以发现,其实并不需要整个数组是有序的,只需要让数组中的0

1. 如果A[i]=n,则continue

2. 如果A[i]=A[A[i]],则continue

3. 如果0<A[i]<n,那么可以还原,则swap(A[i], A[A[i]]),然后跳转到步骤2

然后,数组中如果存在0

  • 从1开始遍历,如果A[i]!=i,则i就是要找的数

  • 如果遍历完,没有找到,则说明i从[1,n-1],都有A[i]=i,那么这个时候就看A[0],如果A[0]是n,则返回n+1,如果A[0]不是n,则返回n,即可。

上面算法的时间复杂度是O(n)么?有的同学会想,在swap之后,仍然到第2步,可能还会出现交换,所以时间复杂度要高于O(n)。但实际上,swap之后,是会发生再次的交换,但我们可以保证,每一次交换,都会使得一个数字还原,即A[i]=i,再一次交换,会让一个新的数字还原。 假设数组中,可以还原的数字个数为k,则需要交换k次,k最多为n-1。这里要很小心,因为总的交换次数是k,并不是对于每一个元素,都会产生k次交换。 如果是后者,总的时间复杂度就是O(kn)了,但因为总的交换次数是k,所以总的时间复杂度是O(n+k),就是O(n)。

//swap: O(n)
int findMissingNum(vector<int> arr)
{
	for(int i = 0; i < arr.size();){
		if(arr[i] == i || arr[i] < 1 || arr[i] >= arr.size() || arr[i] ==arr[arr[i]])
			i++;
		else if(arr[i] < arr.size()){
			int tmp = arr[i];
			arr[i] = arr[tmp];
			arr[tmp] = tmp;
		}else
			i++;
	}
	int i = 1;
	for(; i < arr.size(); i++)
		if(arr[i] != i)
			return i;
	return i;
}




From: http://www.ituring.com.cn/article/56179



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值