[算法]一数组中存有1-10000中除去x,y的其他整数,扫描一遍得出缺失的那两个数x,y

Paradin 2008-11-10 06:30:39
使用的额外空间越少越好
...全文
304 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhijunliu111 2008-11-10
  • 打赏
  • 举报
回复
15楼正解,顶一个
bill830711 2008-11-10
  • 打赏
  • 举报
回复
x+Y
xy=
计算应该可以
R9R9R9 2008-11-10
  • 打赏
  • 举报
回复
题目:

从1到N(100000)中任意拿掉两个数,把剩下的99998个数顺序打乱,并且放入数组A中。要求只扫描一遍数组,把这两个数找出来。可以使用最到不超过5个局部变量,不能用数组变量,并且不能改变原数组的值。
R9R9R9 2008-11-10
  • 打赏
  • 举报
回复
另一个写法,不会产生精度问题

(1+N)*N /2 - S = a + b
1/6 * n*(n + 1)*(2n + 1) - X = a*a + b*b

注:
1/6 * n*(n + 1)*(2n + 1)=1*1 + 2*2 + 3*3 +...+N*N
X = A[0]*A[0] + A[1]*A[1] +...A[N-3]*A[N-3]

==>
a + b = m
a*a + b*b = n

由于可解出a,b


unsigned int sum = 0;
unsigned int sqrSum = 0;

for(i=0; i<N-2; i++)
{
sum += N-i-A[i];
sqrSum += ((N-i)*(N-i)) - ((A[i])*A[i]);

}
sum += 2 + 1;
sqrSum += 2*2 + 1*1;
R9R9R9 2008-11-10
  • 打赏
  • 举报
回复
很早之 前写的,记在blog上,楼主参考一下。

思路:
遍历一次数组,求出这两个数的和a+b 与积a*b
a+b = 1+2+3+4+...+N- sum(A[]); (1)
a*b = 1*2*3*4*...*N / multi(A[]); (2)

主要解决sum与multi的溢出问题

(1) 可化为 (N-A[0]) + (N-1-A[1]) + ...+ (3-A[N-3]) + 2 + 1

(2) 可以用对数来代替原数进行求积的等价运算,避免溢出的问题,但是这种方法会产生一些精度上的问题,不知道大家有什么更好的方法!
先求出log(a*b) :
= log(1*2*3*4*....*N)/log(A[0]*A[1]*A[2]*...*A[N-3])
= log(N)-log(A[0]) + log(N-1)-log(A[1]) + ... +log(3)-log(A[N-3]) + log(2) + log(1)

知道了两数的和与积,由此就可以计算出a跟b的值来.


#include <iostream>
#include <Ctime>
#include <Cmath>
using namespace std;


#define N 100000

//生成不同的随机数的数组
void GetDiffRandomNum(int A[], int n)
{
srand(unsigned(time(NULL)));
int i=0;

for(int index = n-1; index > 0; index--)
{
i = rand() % index;
swap(A[i], A[index]);
}

}


int main()
{

int A[N]={0};
for(int i=0; i<N; i++)
{
A[i] = i+1;
}
GetDiffRandomNum(A, N);
//DISPLAY(A, N);

unsigned int sum = 0;
double logSum = 0;

for(i=0; i<N-2; i++)
{
sum += N-i-A[i];
logSum += log(N-i)-log(A[i]);
}
sum += 2 + 1;
logSum += log(2)+log(1);

double multi = exp(logSum);

//两数的和与积
cout<<int(sum)<<'\t'<<int(multi)<<endl;

//求出两数
for(i=1; i<=N; i++)
{
double temp = i*(sum-i);
if(multi-0.5<=temp && temp <= multi+0.5)
cout<<i<<'\t'<<int(sum-i)<<endl;
}

return 0;
}



xuedaoli 2008-11-10
  • 打赏
  • 举报
回复
如果有序是比较简单的,无序的话就只能先来个排序,应该会快点的

不是有序,先排序
假设nums[9998]是个有序的数组(从小到大)

1.先判断nums[0]是否为1,若大于1,就好搞了
int num1,num2;
int count = 0 ;//统计当前找到的个数
if(nums[0] == 2)
{
num1 = 1;
count++;
//要再去找个
}
else if(num[0] == 3)
{
num1 = 1;
num2 = 2;
count = 2;
}
else if(num[0] > 3)
{
不成立,要找二个

}

if(count != 0 )
{
for(int i=0,j=1; i<9998;i++,j++)
{
if(num[i] == j)
{
count++;
if(count == 1)
num1 = j;
else if(count == 2)
{
num2 = j;break;
}
}
}
if(count == 0)
{
//缺少的9999,10000
cout << 9999<<" "<<10000<<endl;
}
else
cout << num1 << " "<<num2 <<endl;
}




jia_xiaoxin 2008-11-10
  • 打赏
  • 举报
回复
#include <iostream>
using namespace std;
#define N 10000
int main()
{
int Array[N];
int index = 0;
for(int i = 0; i < N; i ++)
{
if(i == 1000 || i == 5000)
{
continue;
}
Array[index] = i;
index++;
}

int a[2] = {0,0};

//通过一次循环查找x,y;
int k = 0;
for(i = 0; i < N - 3; i++)
{
if(Array[i+1] - Array[i] == 2)
{
a[k] = Array[i] + 1;
k++;
}
if(Array[i+1] - Array[i] == 3)
{
a[k] = Array[i] + 1;
a[k+1] = Array[i] + 2;
break;
}
}

if(a[0] == 0 && a[1] == 0)
a[1] = 1;

cout << "要找的两个数为: X = " << a[0] << endl;;
cout << "要找的两个数为: Y = " << a[1] << endl;;
return 0;
}
P_ghost 2008-11-10
  • 打赏
  • 举报
回复
如果是查找一个的话,可以使用XOR运算来解决,两个的话我没有想过
如果是一个:
数组iArr[MAX],已经装好了数MAX = 999
int iResult = 10000;
for(int ix=0; ix<MAX; ++ix)
{
iResult ^= iArr[MAX];
}
iResult ^= 1;
iResult就是所要结果。

两个数的话,自己再考虑吧。
Paradin 2008-11-10
  • 打赏
  • 举报
回复
我有个想法:1-10000所有数的和可以用公式在O(1)算出来:n(n+1)/2,那么扫描一遍后可以得到x,y的和s = x + y
如果可以算出p = xy 的话那么用球根公式算出方程 z^2 - sz + p = 0的两根就得解
但无论l0000!还是log(10000!)都不能事先在常数时间内计算好
cyj626 2008-11-10
  • 打赏
  • 举报
回复
(a+b)²=a²+2ab+b²
(a-b)²=a²-2ab+b²
利用这个看看
Paradin 2008-11-10
  • 打赏
  • 举报
回复
就算使用flag[10000]扫描进行标记,flag数组本身也要再扫描一遍才能得到结果啊
dzc8866 2008-11-10
  • 打赏
  • 举报
回复
只扫描一遍,估计只能用flag[10000]了,

#include <iostream>
using namespace std;

#define NUM 10

int main()
{
int array[NUM]={1,2,3,4,5,5,7,9,9,10};
int flag[NUM]={0,0,0,0,0,0,0,0,0,0};

int i=0;

for(i=0;i<NUM;i++)
{
flag[array[i]-1]=1;
}

for(i=0;i<NUM;i++)
{
if(!flag[i])
{
cout<<(i+1)<<endl;
}
}

return 1;
}
BaihowFF 2008-11-10
  • 打赏
  • 举报
回复
如果只让扫描一边的话,显然就不能用时间换了
那也就只有使用flag[10000]扫描进行标记
如果有序的话就可以不用这么多额外空间...大概3-4个变量就ok了...
Paradin 2008-11-10
  • 打赏
  • 举报
回复
那加入空间要求不高吧
Paradin 2008-11-10
  • 打赏
  • 举报
回复
楼上是?
帅得不敢出门 2008-11-10
  • 打赏
  • 举报
回复
如果元素是混乱无序存放的话
空间又要求要少 只扫描一遍
我想不出来.
macfan 2008-11-10
  • 打赏
  • 举报
回复

int arry[10000];
for(i=0;i<10000;i++)
arry[i]=i+1;
for(j=0;j<10000;j++)
if( j+1 != arry[j] )
cout<<arry[j]<<endl;
太乙 2008-11-10
  • 打赏
  • 举报
回复
那我用时间换空间行么?

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧