【笔试题】找出数组中重复的数字

题目描述

在一个数组长度为n的数组里,所有数字都在 0 ~ n-1 范围内,数组中某些数字是重复的,但不知道几个数字重复了,也不知道每个数字重复了多少次,请找出数组中重复的数字。

思路一:插排思想(比较挫,时间复杂度:n²)

因为要输出任意一个重复的数字,只要一个数字出现次数等于两次,就可以输出这个数了。可以选取第一个数作为基准,依次遍历这个数之后的所有数,如果有相等的数,就直接返回;如果没有,就选取第二个数作为基准,依次遍历第二个数后面的所有数,如果有相等的,就返回。……

思路二:哈希思想(空间换取时间)

开辟一个大小为n的数组HashTable,遍历一遍用户输入的数组,将数组中的元素映射到HashTable数组中,比如:用户数组中有5,7,5,就分别将HashTable中下标为5的位置+1,下标为7的位置+1,下标为5的位置再+1,直到遍历完成。那么每个数的出现次数都有了。

  • 代码写的比较挫,没有按题意写,但是思路是对的。
#include<iostream>
#include<cstdio>
using namespace std;

void Hash_Search(int *arr, int sz)
{
    int HashTable[1024] = { 0 };
    for (int i = 0; i < sz; ++i)
    {
        cout << "存放" << arr[i] << endl;
        HashTable[(arr[i])] ++;
    }

    cout <<  endl;

    for (int j = 0; j < sz; j++)
    {
        cout << "查看" << j << "出现次数: " << HashTable[j] << endl;
    }


    cout << endl;
    for (int j = 0; j < sz; j++)
    {
        if (HashTable[j] >= 2)
        {
            //return arr[j];
            cout << "结果:" << arr[j - 1] << endl;
            exit(0);
        }
    }

}

int main()
{
    int arr[6] = { 1, 4, 3, 3, 2, 3 };
     Hash_Search(arr, 6);
    return 0;
}

这种方法同样可以解决:数组中出现次数超过一半的数,数组中出现了N次的数,字符串中出现N次的字符,员工统计水果问题。

思路三:修改原数组顺序解法(剑指Offer解法)

首先给定数组arr[] = {2, 3, 0, 1, 3}。因为数组中最大的数是n-1,那就一个萝卜一颗坑。从0号下标位置开始,0号元素为2,不等于0,交换0号和2号位置,数组变为:
{0, 3, 2, 1, 3}。再比较0号位置,下标和值相等,往后走到1号下标元素为3,不等于1,交换1号和3号,变成:{0, 1, 2, 3, 3}。再继续,下标来到4号位置,发现元素下标与值不相等,比较4号和3号下标位置,发现元素相等,返回3即可。

#include<iostream>
#include<cstdio>
using namespace std;

int Hash_Search(int *arr, int sz)
{
    if (arr == NULL || sz <= 0)
        return -1;

    for (int i = 0; i < sz; i++)
    {
        if (arr[i] < 0 || arr[i] > sz - 1)
            return -1;
    }

    for (int i = 0; i < sz; i++)
    {
        while (arr[i] != 1)
        {
            if (arr[i] == arr[arr[i]])
            {
                //cout << arr[i] << endl;
                //exit(0);
                return arr[i];
            }

            //交换
            swap(arr[i], arr[arr[i]]);

        }
    }
    return -1;
}

int main()
{
    int arr[6] = { 1, 4, 0, 2, 2, 3 };
     int GetNum = Hash_Search(arr, 6);
    cout <<"输出:" <<GetNum << endl;
    return 0;
}
思路四:不修改原数组顺序解法(剑指Offer解法:分割统计次数)

不高明解法:

不修改原数组可以创建一个复制空间,大小为n,在辅助空间上按思路三求解也可以,但是需要O(n)的辅助空间。

高明解法:

首先将数组一分为二,假设数组有8个元素。那么元素大小都在 0 ~ 7 之间。一分为二,如果没有重复,四个元素在 0 ~ 3 之间,四个在 4 ~ 7 之间。假设数组为arr[8] = {1, 0, 2, 3, 3, 4, 5, 6},统计0 ~ 3 之间的元素有5个, 统计 4 ~ 7 之间的元素有3个。说明,在 0 ~ 3 之间肯定有重复元素。接下来划分 0 ~ 3的5个元素{1, 0, 2, 3, 3}。 0 ~ 1 之间有两个元素, 2~3之间有三个元素。再划分2 ~ 3之间的元素,2有一个,3有两个,所以重复的是元素3。

和不高明解法相比,这是以时间换区空间。

#include <iostream>
#include <vector>
using namespace std;

int countrange(vector<int> vec, int begin, int end, int length)
{
    int count = 0;
    for (int i = 0; i<length; ++i)
    {
        if (vec[i] >= begin && vec[i] <= end)
            ++count;
    }
    return count;
}

int duplication(vector<int> vec)
{
    // 条件检测
    int length = vec.size();
    if (vec.size() == 0)
        return -1;

    for (int i = 0; i<length; ++i)
    {
        if (vec[i]<1 || vec[i]>length - 1)
            return -1;
    }

    int begin = 1;
    int end = length - 1;

    while (begin <= end)
    {
        int mid = ((end - begin) >> 1) + begin;
        int count = countrange(vec, begin, mid, length);

        if (end > begin)
        {
            // 更新数字范围
            if (count>(mid - begin + 1))
                end = mid;
            else
                begin = mid + 1;
        }
        else
        {
            if (count > 1)
                return begin;
            else
                break;
        }
    }

    return -1;
}

int main()
{
    vector<int> vec = { 2, 2, 3, 3, 4, 5, 6 };
    int num = duplication(vec);
    cout << "结果:" << num << endl;
    return 0;
}
  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

askunix_hjh

谢谢请我吃糖~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值