每日一题(23) - 找出数组中重复的数据和丢失的数据。

题目来自于网上

题目:

一个大小为n的数组,元素范围为1-n,但是其中一些数组有重复,同时另一些数据丢失了。 现在要求找出其中的重复数据和丢失的数据。

要求:

时间O(n),空间O(1),即,只能遍历常数次数组,只能使用常数个临时空间

思想:

利用计数排序的思想,把数组的第i个数对应着数组i,并统计其出现次数。原始的计数排序需要O(n)的临时空间。这里要求空间复杂度为O(n),即使用传统的计数排序是不行的。这里可以使用原数组的计数排序。

基于原数组的计数排序的思想:

当处理数组第i个数arr[i]时,

(1)如果元素arr[arr[i]]放的数不是arr[i],即arr[i]没有放到它应该在的位置。则此时把arr[i]放到arr[arr[i]]的位置上。于此同时,还需要把原来arr[arr[i]]存放的元素放到arr[i],之后继续处理元素arr[i](此时的arr[i]是刚换过来的元素)。一直到arr[i]存放的元素是arr[arr[i]]为止。之后继续处理第i+1个数。

注意,每进行一次元素互换都使一个数放到正确的位置上。

(2)如果元素arr[arr[i]]放的数已经是arr[i],则表示arr[i]已经在它正确的位置上了。之后继续处理第i+1个数。

(3)重复执行(1)和(2),一直到所有的元素均处理完毕。

注意:直到arr[i]放到arr[arr[i]]上时,i才会++。

结果分析:

对于数组中的数据:

如果数组 i 位置放的是a[i],即,a[i]放到了正确的位置。

如果数组 i 位置放的不是a[i],但是程序结束的情况是所有的a[i]均放到起正确的位置(a[a[i]])。

则,对于位置i,肯定是在该数组中没出现i这个数。

对于a[i],由于这个a[i]未放到正确的位置(a[a[i]]),说明之前已经有a[i]放到正确的位置(a[a[i]])上了,则表示该数a[i]重复了。

举例:

待处理的数组:5 1 2 2 3

i = 1,处理a[1] = 5时,它本应该在的位置存放的是3,则互换位置,即 3 1 2 2 5

i = 1,处理a[1] = 3时,它本应该在的位置存放的是2,则互换位置,即 2 1 3 2 5

i = 1,处理a[1] = 2时,它本应该在的位置存放的是1,则互换位置,即 1 2 3 2 5

i = 1,处理a[1] = 1时,它本应该在的位置存放的是1,则位置正确,则i++

i = 2,处理a[2] = 2时,它本应该在的位置存放的是2,则位置正确,则i++

i = 3,处理a[3] = 3时,它本应该在的位置存放的是3,则位置正确,则i++

i = 4,处理a[4] = 2时,它本应该在的位置存放的是2,则位置正确,且此时的数应该是重复的。此时i++

i = 5,处理a[5] = 5时,它本应该在的位置存放的是5,则位置正确,此时i++

代码:

#include <iostream>
#include <assert.h>
using namespace std;

void FindNum(int arr[],int nLen,int nStart,int nEnd)
{
	assert(arr != NULL && nStart <= nEnd && nStart >= 0 && nEnd >= 0);
	int nTmp = 0;
	for (int i = 1;i <= nLen;i++)
	{
		assert(arr[i] >= 1 && arr[i] <= nLen);
		while (arr[arr[i]] != arr[i])
		{
			//arr[i]放的位置不对,则需要把Arr[i]放到正确的位置
			//此时需要把 arr[i] 放到他原本的位置arr[arr[i]]上
			//注意这里肯不是直接覆盖,而是替换
			nTmp = arr[arr[i]];
			arr[arr[i]] = arr[i];
			arr[i] = nTmp;
		}
	}
	for (int i = nStart;i <= nLen;i++)
	{
		if (i != arr[i])
		{
			cout<<"缺失的数: "<<i<<endl;
			cout<<"重复的数: "<<arr[i]<<endl;
		}
	}
	cout<<endl;
}

int main()
{
	int arr[5 + 1] = {0,5,1,2,2,3};//第一个位置不用
	FindNum(arr,5,1,5);
	system("pause");
	return 1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值