一道算法题目

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

我的解法:
1、省空间,费时间:排序,然后扫描数组,
2、以空间换时间,开辟大小为n的数组,可以在O(n)时间内完成。

但是面试官要求优化到时间O(n)空间O(1),考虑用位图的方法,空间是小了些,变成O(n/32),但是这还是O(n)而不是O(1)。

有人知道的说下?
...全文
530 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
lennydou 2013-05-15
  • 打赏
  • 举报
回复
这个题没有什么难的,只要允许在原数组上进行操作,就可以保证空间复杂度是O(1)了。 1. 选择一个1 到 n以外的变量VISITED,然后定义一个变量temp。假设输入数组是data 2. 使用变量i从1到n遍历该数组,如果当前元素不是VISITED,取出当前元素值赋给temp;否则继续遍历下个元素。遍历完成后跳转到步骤4 3. 如果data[temp]的值是VISITED,则输出该元素为重复元素,跳转到2继续遍历下一个元素;否则,取出data[temp]的值赋给temp2,然后把data[temp]的值赋为VISITED,然后temp=temp2. 接着重复执行步骤3。 4. 重新遍历data,未被标识为VISITED的元素的下标i, 就是缺失的数据。
赵4老师 2013-05-15
  • 打赏
  • 举报
回复
仅供参考
//随机产生100000000个取值范围为[0~2的32次方减1]的数据,
//然后让用户输入一个数据,判断用户输入的数据是不是包含在前面随机产生的数据中。
//要求:当用户输入完成后,必须在1毫秒(千分之一秒)之内完成判断。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
long int i;
unsigned long ul;
unsigned long *d;
unsigned long ulrand(void) {
    return (
     (((unsigned long)rand()<<24)&0xFF000000ul)
    |(((unsigned long)rand()<<12)&0x00FFF000ul)
    |(((unsigned long)rand()    )&0x00000FFFul));
}
void main() {
    d=(unsigned long *)calloc(1<<(32-5),sizeof(unsigned long));
    if (NULL==d) {
        printf("Can not calloc(%d,%d)!\n",1<<(32-5),sizeof(unsigned long));
        return;
    }
    srand(time(NULL));
    for (i=0;i<100000000;i++) {
        while (1) {
            ul=ulrand();
            if (0==(d[ul>>5]&(1lu<<(ul%32)))) {
                d[ul>>5]|=1<<(ul%32);
                break;
            }
        }
        if (0==i%1000000) printf("%09d,%10lu\n",i,ul);
    }
    while (1) {
        printf("\nInput a number:");
        fflush(stdout);
        rewind(stdin);
        if (1==scanf("%lu",&ul)) break;
    }
    if (d[ul>>5]&(1<<(ul%32))) printf("Include.\n"    );
    else                       printf("Not include.\n");
    free(d);
}
//000000000,2135468533
//001000000,2465805973
//002000000,3844079964
//003000000,1883232874
//004000000,1204697784
//005000000,4050838287
//006000000,3802081245
//007000000,1586042671
//008000000,3119931368
//009000000, 251096899
//010000000,3491239701
//011000000,3365323844
//012000000,2191846708
//013000000,1879478195
//014000000,1112631457
//015000000,1927301519
//016000000,1717332861
//017000000,2922278240
//018000000, 694854106
//019000000, 273255526
//020000000, 398518467
//021000000,3270756812
//022000000,1500289424
//023000000,1502241936
//024000000,1770380660
//025000000,3668842116
//026000000,3255869879
//027000000,1299184024
//028000000,1072990028
//029000000, 242094712
//030000000,3789344297
//031000000,2599365925
//032000000, 962754138
//033000000,2055075654
//034000000,4083452879
//035000000, 489250842
//036000000, 611455230
//037000000, 277350616
//038000000,1597410795
//039000000,3224173662
//040000000,2291446877
//041000000,2546280575
//042000000,2509145642
//043000000,2371773252
//044000000, 635555963
//045000000,2674538666
//046000000,4253690312
//047000000,2675755514
//048000000,1269320296
//049000000,3172516920
//050000000,1430265210
//051000000, 196156173
//052000000,2470825669
//053000000,2536750977
//054000000,1182829949
//055000000,3202826434
//056000000,2263336265
//057000000, 313302924
//058000000,3630264578
//059000000,1154892716
//060000000,2985304230
//061000000,1252204837
//062000000,1292076720
//063000000, 242249250
//064000000,3999999961
//065000000, 431166416
//066000000,1366947236
//067000000,1414387330
//068000000,2143784481
//069000000,3242175409
//070000000,4158065163
//071000000,1425449573
//072000000,2493600232
//073000000,1316783455
//074000000,3723170478
//075000000,3064111466
//076000000, 408557403
//077000000,3722586955
//078000000,3801652651
//079000000,3788160154
//080000000,3329440047
//081000000,1408976868
//082000000, 471838899
//083000000,2145198260
//084000000,3781081738
//085000000,3439027738
//086000000,1150808750
//087000000,2782578638
//088000000,  85604584
//089000000,2704078162
//090000000, 584840269
//091000000,3854577719
//092000000,2823653537
//093000000, 797877025
//094000000,2248017755
//095000000,1787038685
//096000000,2816548567
//097000000, 489107494
//098000000, 911680090
//099000000,3677777147
//
//Input a number:3677777147
//Include.
wxsxiaoK 2013-05-14
  • 打赏
  • 举报
回复
见笑了! 其实我上面那个只是凭印象写的,没法证明复杂度O(n),好像涉及到离散数学中群之类的东西,我之前看过一个文章,这种思想好像是一个微软哥们弄出来的,还有论文呢(大公司员工就是厉害),高等数学水平有限,没太看懂证明。
darkmourf 2013-05-14
  • 打赏
  • 举报
回复
引用 23 楼 lxhscx 的回复:
貌似10楼的代码已经可以说明思路了呵呵 先说明一下,我说的计数排序和hash并非就是说本题就是用这个来做,而是说有类似的地方,如果看过《编程珠玑》第一章,我真觉得不可能解不出来这题。 有人说到排序是nlogn,这个是基于交换的排序。计数排序不是基于交换的,所以不受这个限制。 什么是计数排序?简单从这个题目看,如果是1-n没有重复,那么还需要排序吗?是不是直接输出1-n就成?如果不考虑空间要求,那么计数排序实现为: 1.声明1-n的辅助空间 2.遍历原数组,记录1-n各数字出现的个数到对应辅助下标; 3.遍历辅助空间,输出内部不为0的元素 比如 3,2,4,4,4,5,8,2,7,3 通过步骤2后辅助空间中存的为0,2,2,3,1,0,1,1,0,0 步骤3就可以输出2 2 3 3 4 4 4 5 7 8 排序复杂度为O(n). 此题中还增加了辅助空间O(1)的要求,那么就是原地排么 10楼的程序已经体现了这个思想,最后面原数组里面放的就是下标i中放的要么是i,要么是其它数据,下标i中如果不是i那么说明出现了重复。 也就是: 1.排列原数组,使i下标对应i; 2.遍历原数组,输出i下标不是i的元素; 请看步骤1后的原数组,如果实现过地址探测法的hash表,是不是觉得这种排列类似?只不过是个hash函数为return i;的hash表而已。 另外,请想一想楼10中的代码,为啥可以保证while循环中一定会结束呢?该循环的次数为啥不会使算法复杂度超过O(n)呢? 我还是那句话,《编程珠玑》值得一看! ps:个人水平有限,觉得自己讲肯定不如经典的书讲得好,所以我宁愿推荐书而不愿意自己讲,或许这样不是一些人看到的直观的答案,但看书自己探索的过程才真有收益!
学习了
quickSort 2013-05-14
  • 打赏
  • 举报
回复
引用 23 楼 lxhscx 的回复:
貌似10楼的代码已经可以说明思路了呵呵 先说明一下,我说的计数排序和hash并非就是说本题就是用这个来做,而是说有类似的地方,如果看过《编程珠玑》第一章,我真觉得不可能解不出来这题。 有人说到排序是nlogn,这个是基于交换的排序。计数排序不是基于交换的,所以不受这个限制。 什么是计数排序?简单从这个题目看,如果是1-n没有重复,那么还需要排序吗?是不是直接输出1-n就成?如果不考虑空间要求,那么计数排序实现为: 1.声明1-n的辅助空间 2.遍历原数组,记录1-n各数字出现的个数到对应辅助下标; 3.遍历辅助空间,输出内部不为0的元素 比如 3,2,4,4,4,5,8,2,7,3 通过步骤2后辅助空间中存的为0,2,2,3,1,0,1,1,0,0 步骤3就可以输出2 2 3 3 4 4 4 5 7 8 排序复杂度为O(n). 此题中还增加了辅助空间O(1)的要求,那么就是原地排么 10楼的程序已经体现了这个思想,最后面原数组里面放的就是下标i中放的要么是i,要么是其它数据,下标i中如果不是i那么说明出现了重复。 也就是: 1.排列原数组,使i下标对应i; 2.遍历原数组,输出i下标不是i的元素; 请看步骤1后的原数组,如果实现过地址探测法的hash表,是不是觉得这种排列类似?只不过是个hash函数为return i;的hash表而已。 另外,请想一想楼10中的代码,为啥可以保证while循环中一定会结束呢?该循环的次数为啥不会使算法复杂度超过O(n)呢? 我还是那句话,《编程珠玑》值得一看! ps:个人水平有限,觉得自己讲肯定不如经典的书讲得好,所以我宁愿推荐书而不愿意自己讲,或许这样不是一些人看到的直观的答案,但看书自己探索的过程才真有收益!
多谢……
quickSort 2013-05-14
  • 打赏
  • 举报
回复
引用 18 楼 haierpro 的回复:

for (int i = 1;i <= n;i++)
{
	if (a[a[i]] != a[i])
	{
		tmp = a[i];
		a[i] = a[tmp];
		a[tmp] = tmp;
	}
}
	
for (int i = 1;i <= n;i++)
{
	if (a[i] != i)
	{
		//a[i]为重复的数据
		//i为缺失的数据
	}
}

for (int i = 1;i <= n;i++)
{
	if (a[a[i]] != a[i])///这里改成while就好了。。。
	{
		tmp = a[i];
		a[i] = a[tmp];
		a[tmp] = tmp;
	}
}
	
for (int i = 1;i <= n;i++)
{
	if (a[i] != i)
	{
		//a[i]为重复的数据
		//i为缺失的数据
	}
}
参考十楼的代码。。。
剑虹-思静 2013-05-14
  • 打赏
  • 举报
回复
貌似10楼的代码已经可以说明思路了呵呵 先说明一下,我说的计数排序和hash并非就是说本题就是用这个来做,而是说有类似的地方,如果看过《编程珠玑》第一章,我真觉得不可能解不出来这题。 有人说到排序是nlogn,这个是基于交换的排序。计数排序不是基于交换的,所以不受这个限制。 什么是计数排序?简单从这个题目看,如果是1-n没有重复,那么还需要排序吗?是不是直接输出1-n就成?如果不考虑空间要求,那么计数排序实现为: 1.声明1-n的辅助空间 2.遍历原数组,记录1-n各数字出现的个数到对应辅助下标; 3.遍历辅助空间,输出内部不为0的元素 比如 3,2,4,4,4,5,8,2,7,3 通过步骤2后辅助空间中存的为0,2,2,3,1,0,1,1,0,0 步骤3就可以输出2 2 3 3 4 4 4 5 7 8 排序复杂度为O(n). 此题中还增加了辅助空间O(1)的要求,那么就是原地排么 10楼的程序已经体现了这个思想,最后面原数组里面放的就是下标i中放的要么是i,要么是其它数据,下标i中如果不是i那么说明出现了重复。 也就是: 1.排列原数组,使i下标对应i; 2.遍历原数组,输出i下标不是i的元素; 请看步骤1后的原数组,如果实现过地址探测法的hash表,是不是觉得这种排列类似?只不过是个hash函数为return i;的hash表而已。 另外,请想一想楼10中的代码,为啥可以保证while循环中一定会结束呢?该循环的次数为啥不会使算法复杂度超过O(n)呢? 我还是那句话,《编程珠玑》值得一看! ps:个人水平有限,觉得自己讲肯定不如经典的书讲得好,所以我宁愿推荐书而不愿意自己讲,或许这样不是一些人看到的直观的答案,但看书自己探索的过程才真有收益!
nice_cxf 2013-05-14
  • 打赏
  • 举报
回复
大概可以用完美洗牌的思路 顺序遍历数组,如果a[n]==n+1则继续,如果不相等 tmp=a[n]-1看a[tmp]的数据,如果为0,替换为tmp继续,如果是tmp+1,表明这个是重复点,输出后继续,如果是其他数字,记录该数字,然后继续处理 最后遍历一遍数组,值为0的表示是缺失的点
海的神话 2013-05-14
  • 打赏
  • 举报
回复
之前好像见过类似的题目,可以看看这位老兄整理的算法题目,好像有讲到的 http://blog.csdn.net/v_JULY_v/article/details/6057286
nice_cxf 2013-05-14
  • 打赏
  • 举报
回复
引用 13 楼 nice_cxf 的回复:
[quote=引用 12 楼 lxhscx 的回复:] 回复8楼、9楼,你们确认看了我提到的几本书吗? 注意题中的限制条件:元素范围1-n,数组大小也是1-n,空间要求O(1)只不过是原地count sorting而已。 hash就是某种形式到下标的映射,重要的是思想,而非背书。 不知道我这样说清楚了没有,如果真还没清楚,那就上代码吧!
排序时间复杂度至少nlogn把?不过题目倒没说时间复杂度为O(n)... [/quote] 错了啊,题目要求时间复杂度为O(n),这样不能排序把?
nice_cxf 2013-05-14
  • 打赏
  • 举报
回复
引用 12 楼 lxhscx 的回复:
回复8楼、9楼,你们确认看了我提到的几本书吗? 注意题中的限制条件:元素范围1-n,数组大小也是1-n,空间要求O(1)只不过是原地count sorting而已。 hash就是某种形式到下标的映射,重要的是思想,而非背书。 不知道我这样说清楚了没有,如果真还没清楚,那就上代码吧!
排序时间复杂度至少nlogn把?不过题目倒没说时间复杂度为O(n)...
quickSort 2013-05-14
  • 打赏
  • 举报
回复
引用 18 楼 haierpro 的回复:

for (int i = 1;i <= n;i++)
{
	if (a[a[i]] != a[i])
	{
		tmp = a[i];
		a[i] = a[tmp];
		a[tmp] = tmp;
	}
}
	
for (int i = 1;i <= n;i++)
{
	if (a[i] != i)
	{
		//a[i]为重复的数据
		//i为缺失的数据
	}
}
我写了个例子,
void GGG()
{
    int a[]={-1,1,2,3,4,5,6,8,9,8};///a[0]not use
    int n = sizeof(a)/sizeof(int)-1;
    for(int i=1;i<=n;i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<"  n = "<<n<<endl;
    for(int i=1;i<=n;i++)
    {
        if(a[a[i]]!=a[i])
        {
            int t = a[i];
            a[i] = a[t];
            a[t] = t;
        }
    }
    cout<<"miss\t";
    cout<<"replica\t"<<endl;
    for(int i=1;i<=n;i++)
    {
        if(a[i]!=i)
        {
            cout<<i<<"\t";
            cout<<a[i]<<endl;
        }
    }


}
运行结果:
1 2 3 4 5 6 8 9 8   n = 9
miss    replica
7       9
9       8

Process returned 0 (0x0)   execution time : 0.043 s
Press any key to continue.
u010693912 2013-05-14
  • 打赏
  • 举报
回复
有难度找专家吧 签名: 快乐云学习,www.yxtkj.net,课外辅导,1183180
shadowalker911 2013-05-14
  • 打赏
  • 举报
回复
这个不能有结果。
bfdeh 2013-05-14
  • 打赏
  • 举报
回复
引用 18 楼 haierpro 的回复:

for (int i = 1;i <= n;i++)
{
	if (a[a[i]] != a[i])
	{
		tmp = a[i];
		a[i] = a[tmp];
		a[tmp] = tmp;
	}
}
	
for (int i = 1;i <= n;i++)
{
	if (a[i] != i)
	{
		//a[i]为重复的数据
		//i为缺失的数据
	}
}
haierpro 2013-05-14
  • 打赏
  • 举报
回复

for (int i = 1;i <= n;i++)
{
	if (a[a[i]] != a[i])
	{
		tmp = a[i];
		a[i] = a[tmp];
		a[tmp] = tmp;
	}
}
	
for (int i = 1;i <= n;i++)
{
	if (a[i] != i)
	{
		//a[i]为重复的数据
		//i为缺失的数据
	}
}
十八道胡同 2013-05-14
  • 打赏
  • 举报
回复
好像就是洗牌吧 因为a[x] 应该是= x + 1 1.如果==,继续搜索 2.如果!=,那么假设tmp= a[x], 此时搜索a[tmp-1] 同时把a[x] 设置成0,如果a[tmp-1] = tmp - 1 + 1,那么这个数重复值,输出。如果a[tmp-1] != tmp -1+1,重复第2步 最后走一边,是0的就是缺失的
剑虹-思静 2013-05-13
  • 打赏
  • 举报
回复
回复8楼、9楼,你们确认看了我提到的几本书吗? 注意题中的限制条件:元素范围1-n,数组大小也是1-n,空间要求O(1)只不过是原地count sorting而已。 hash就是某种形式到下标的映射,重要的是思想,而非背书。 不知道我这样说清楚了没有,如果真还没清楚,那就上代码吧!
derekrose 2013-05-13
  • 打赏
  • 举报
回复
引用 6 楼 hello_world_2012 的回复:
[quote=引用 4 楼 derekrose 的回复:] 一开始我想到了加法,后来发现行不通,然后我想到了乘法,然后我发现约分是一个很痛苦的过程,那么求因子也是一个很痛苦的过程,但是这个最多需要O(n)的倍数,而且要满足因子数目相同,而且因子最后要去和原始数组比对。 这题很难,我想到这些了,既然你参加了MS的面试,肯定可以理解的!
加法和异或运算都想到了,但是貌似不行, 开始感觉这道题目是以前某个数变了的变种。。。[/quote] 我说的乘法
wxsxiaoK 2013-05-13
  • 打赏
  • 举报
回复
写了一个不知道对不对

int temp=0;
	int myArray[10]={8,7,8,5,8,2,4,8,1,2};
	for(int i=0;i<10;)
	{
		if(i==myArray[i])
		{
			i++;
		}
		else
		{			
			while(i!=myArray[i])
			{
				if(myArray[myArray[i]]==myArray[i])
				{
					i++;
					break;
				}
				temp=myArray[myArray[i]];
				myArray[myArray[i]]=myArray[i];
				myArray[i]=temp;
			}
		}
	}
加载更多回复(9)

64,281

社区成员

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

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