找出一个重复元素

转自出处


1. 问题描述

取值为[1,n-1]含n个元素的整数数组至少存在一个重复数,O(n)时间内找出其中任意一个重复数。如a[]={1,2,2,4,5,4},则2和4均是重复元素。

2. 解决方案

【方案一】

<采用位图> 使用大小为N位图,记录每个元素是否出现过,一旦遇到一个已经出现过的元素,则直接输出。时间复杂度是O(N), 空间复杂度为O(N)。

<数组排序>首先对数组进行计数排序,然后顺次扫描整个数组,直到遇到一个已出现的元素,直接将之输出。时间复杂度为O(N),空间复杂度为O(N)。

以上两种方案需要额外的存储空间,能不能不适用额外存储空间呢?

【方案二】

 首先问面试官,以确定数组元素是否为signed int,如果是,则可采用该方案:将数组元素值作为索引,对于元素a[i],如果a[a[i]]大于0,则设置a[a[i]]=- a[a[i]];如果a[a[i]]小于0,则a[a[i]]是一个重复数,直接输出,最后还原a中各个被修改的元素。代码如下:

 
  

int find_duplicated_integer(int a[], int n) {
int i;
for(i = 0; i < n; i++) {
if(a[i] > 0) {
if(a[a[i]] > 0) {
a[a[i]] = -a[a[i]];
} else {
return -a[a[i]];
}
} else {
if(a[-a[i]] > 0) {
a[-a[i]] = -a[-a[i]];
} else {
return -a[-a[i]];
}
}
}
}

int reset_array(int a[], int n) {
int i;
for(i = 0; i < n; i++) {
if(a[i] < 0)
a[i] = -a[i];
}
}


【方案三】

<单链表存在环> 第三种解决方案有很强的技巧性。“判断单链表是否存在环”是一个非常经典的问题,同时单链表可以采用数组实现,此时每个元素值作为next指针指向下一个元素。该题可以转化为“已知一个单链表中存在环,找出环的入口点”。

该题思路如下:将a[i]看做第i个元素的索引,即:a[i]->a[a[i]]->a[a[a[i]]]->a[a[a[a[i]]]]->….最终形成一个单链表,由于数组a中存在重复元素,则一定存在一个环,且环的入口元素即为重复元素。

该题的关键在于,数组a的大小时n,而元素的范围是[1,n-1],所以a[0]不会指向自己,进而不会陷入错误的自循环。如果元素的范围中包含0,则该题不可直接采用该方法。

代码如下:

 
  

int find_duplicated_integer2(int a[], int n) {
int x, y;
x = y = 0;
do {
x = a[a[x]]; //x一次走两步
y = a[y]; //y一次走一步
} while(x != y); //找到环中的一个点
x = 0;
do {
x = a[x];
y = a[y];
} while(x != y); //找到入口点
return x;
}


你可能感兴趣的:(算法,C++/C)