面试题:数组中找重复元素

题目为:一个大小为n的数组,里面的数都属于范围[0, n-1],有不确定的重复元素,找到至少一个重复元素,要求O(1)空间O(n)时间。

在博客:http://blog.csdn.net/morewindows/article/details/8204460  和 http://blog.csdn.net/morewindows/article/details/8212446 中给出了几种有效的解法。


解法二(类基数排序)的思路:

  下面以2,4,1,5,7,6,1,9,0,2这十个数为例,展示下如何用基数排序来查找重复元素。

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  2

  4

  1

  5

  7

  6

  1

  9

  0

  2

(1)由于第0个元素a[0] 等于2不为0,故交换a[0]与a[a[0]]即交换a[0]与a[2]得:

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  1

  4

  2

  5

  7

  6

  1

  9

  0

  2

(2)由于第0个元素a[0] 等于1不为0,故交换a[0]与a[a[0]]即交换a[0]与a[1]得:

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  4

  1

  2

  5

  7

  6

  1

  9

  0

  2

(3)由于第0个元素a[0] 等于4不为0,故交换a[0]与a[a[0]]即交换a[0]与a[4]得:

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  7

  1

  2

  5

  4

  6

  1

  9

  0

  2

(4)由于第0个元素a[0] 等于7不为0,故交换a[0]与a[a[0]]即交换a[0]与a[7]得:

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  9

  1

  2

  5

  4

  6

  1

  7

  0

  2

(5)由于第0个元素a[0] 等于9不为0,故交换a[0]与a[a[0]]即交换a[0]与a[9]得:

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  2

  1

  2

  5

  4

  6

  1

  7

  0

  9

(6)由于第0个元素a[0] 等于2不为0,故交换a[0]与a[a[0]]即交换a[0]与a[2],但a[2]也为2与a[0]相等,因此我们就找到了一个重复的元素——2。

下标

  0

  1

  2

  3

  4

  5

  6

  7

  8

  9

数据

  2

  1

  2

  5

  4

  6

  1

  7

  0

  9



解法二、三(符号位标记法及改进版)的思路:

设int a[] = {1, 2, 1}

    第一步:由于a[0]等于1大于0,因此先判断下a[a[0]]即a[1]是否小于0,如果小于,说明这是第二次访问下标为1的元素,表明我们已经找到了重复元素。不是则将a[a[0]]取负,a[1]=-a[1]=-2。

    第二步:由于a[1]等于-2,因此先判断下a[-a[1]]取出a[2]是否小于0,如果小于,说明这是第二次访问下标为2的元素,表明我们已经找到了重复元素。不是则将a[-a[1]]取负,a[2]=-a[2]=-1。

    第三步:由于a[2]等于-1,因此判断下a[-a[2]]即a[1]是否小于0,由于a[1]在第一步中被取反过了,因此证明这是第二次访问下标为1的元素,直接返回-a[2]即可。


但是,这种通过取负来判断元素是否重复访问的方法,当只有0是重复元素时是无法找出正确解的。

改进一下也很简单——不用取负,而用加n。这样通过判断元素是否大于等于n就能决定这个元素是未访问过的数据还是已访问过的数据。


全部代码:

#include <iostream>
#include <hash_set>
/*
运行环境:codeblocks
*/
using namespace __gnu_cxx;
using namespace std;

/*
解法一:采用 哈希表,时间复杂度符合要求,但是显然空间复杂度不符要求
*/
void func_1(int *a,int n)
{
    hash_set<int> mySet;
    bool flag = false;
    for(int i=0; i<n; i++)
    {
        hash_set<int>::iterator iter = mySet.find(a[i]);
        if(iter!=mySet.end()) //存在
        {
            flag = true;
            cout<<"exist = "<<a[i]<<endl;
        }
        else
        {
            mySet.insert(a[i]);
        }
    }
    if(!flag)
    {
        cout<<"not exits!"<<endl;
    }
}

void Swap(int &a,int &b)
{
    a = a^b;
    b = a^b;
    a = a^b;
}
/*
解法二:类基数排序法,该方法只能找出序列中一个重复的元素(算是一个小缺陷)
*/
void func_2(int *a,int n)
{
    bool flag = false;
    for(int i=0; i<n; i++)
    {
        while(i != a[i])
        {
            if(a[i] == a[a[i]])
            {
                flag = true;
                cout<<"exist = "<<a[i]<<endl;
                return;
            }
            else
            {
                Swap(a[i],a[a[i]]);
            }
        }
    }
    if(!flag) cout<<"not exist!"<<endl;
}

/*
符号位标记法:对于重复元素为 0 ,则不能满足要求
*/
void tempFunc(int *a,int n)
{
    bool flag = false;
    for(int i = 0; i < n; i++)
    {
        if(a[i] > 0) //判断条件
        {
            if(a[a[i]] < 0)
            {
                flag = true;
                cout<<"exist = "<<a[i]<<endl;//已经被标上负值了,即有重复
            }
            else
            {
                a[a[i]]= -a[a[i]]; //记为负
            }

        }
        else // 此时|a[i]|代表的值已经出现过一次了
        {
            if(a[-a[i]] < 0)
            {
                flag = true;
                cout<<"exist = "<<-a[i]<<endl;//有重复找到
            }
            else
            {
                a[-a[i]] = -a[-a[i] ];  //记为负
            }
        }
    }
    if(!flag)//数组中没有重复的数
    {
        cout<<"not exist!"<<endl;
    }
}

/*
解法三:符号位标记法的改进版:满足要求。
*/
void func_3(int *a,int n)
{
    int index;
    bool flag = false;
    for(int i=0; i<n; i++)
    {
        if(a[i] >= n) index = a[i] - n;
        else index = a[i];

        if(a[index] >= n) //这个位置上的值大于n说明已经是第二次访问这个位置了
        {
            flag = true;
            cout<<"exist = "<<index<<endl;
        }
        else a[index] += n;
    }
    if(!flag)//数组中没有重复的数
    {
        cout<<"not exist!"<<endl;
    }
}

int main()
{
    const int MAXN = 10;
    int a[MAXN] = {0, 4, 1, 5, 7,  6, 1, 9, 0, 2};

    func_3(a,MAXN);

    return 0;
}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值