关于C++数组中重复的数字

 更新时间:2021年11月03日 10:47:09   作者:zx255  
这篇文章主要介绍得是关于C++数组中重复的数字,文章以问题描述得形式,对问题展开分析用不同得方法去解决问题并附上方法得详细代码,需要的朋友可以参考以下文章得具体内容

1、题目描述

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

请找出数组中任意一个重复的数字。

题目示例:

  • 输入:[2, 3, 1, 0, 2, 5, 3]
  • 输出:2 或 3

1.1 方法一:排序

先对数组进行排序
此时从头到尾扫一遍数组就可以了
时间复杂度 O ( l o g 2 n ) O(log_2n) O(log2​n)

代码示例:

int repeatNum(vector<int>& v){
    if(v.empty()) return -1;
    int len = v.size();
    sort(v.begin(), v.end());
    for(int i = 1; i < len; i++){
        if(v[i] == v[i-1]){
            return v[i];
        }
    }
    return -1;
}

1.2 方法二:哈希表

  • 从头到尾扫一遍数组
  • 每扫到一个数字,判断哈希表里是否包含了该数字
  • 如果还没有,就把它加入哈希表中
  • 如果已经存在该数字,就找到了一个重复的数字。

时间复杂度 O ( n ) O(n) O(n) 、空间复杂度 O ( n ) O(n) O(n) ,提高时间效率是以创建一个 O ( n ) O(n) O(n) 的哈希表为代价的。

代码示例:

int repeatNum(vector<int>& v){
    if(v.empty()) return -1;
    map<int, int> m;
    for(int i = 0; i < v.size(); i++){
        if(m[v[i]]) return v[i];
        else m[v[i]]++;
    }
    return -1;
}

1.3 方法三:数组位置交换

  • 从头到尾扫描数组
  • 当扫描的数组下标为 i 时,判断i这个位置的数字 (m) 是否等于 i 本身
  • 若是则扫描下一个数字
  • 若不是则判断 m 和 下标为 m 的数字是否相同 (v[i] == v[v[i]])
  • 若相同则返回,循环结束
  • 若不同则把第 i 个数字 (m) 和 第 m 个数字交换
  • 然后重复这个过程,直至循环结束

时间复杂度为 O ( n ) O(n) O(n) ,空间复杂度为 O ( 1 ) O(1) O(1)

代码示例:

int repeatNum(vector<int>& v){
    if(v.empty()) return -1;
    for(int i = 0; i < v.size(); ++i){
        if(v[i] < 0 || v[i] > v.size()-1) // 数字必须在 0 ~ n-1 之间
            return -1;
    }
    
    for(int i = 0; i < v.size(); ++i){
        while(v[i] != i){
            if(v[i] == v[v[i]]) return v[i];
            swap(v[i], v[v[i]]);
        }
    }
    return false;
}

2、题目升级

长度为 n+1 的数组,所有的数都在 1 ~ n 的范围内,因此数组中至少有一个数字是重复的。找出数组中 任意一个 重复的数字,但 不能修改输入的数组。

题目示例:

  • 输入:[2, 3, 5, 4, 3, 2, 6, 7]
  • 输出:2 或 3

2.1 方法一:哈希表

方法同上:

int repeatNum(vector<int>& v){
    if(v.empty()) return -1;
    map<int, int> m;
    for(int i = 0; i < v.size(); i++){
        if(m[v[i]]) return v[i];
        else m[v[i]]++;
    }
    return -1;
}

2.2 方法二:辅助数组

  • 创建一个长度为 n+1 的辅助数组,然后逐一的把原数组的每个数字复制到辅助数组中
  • 若原数组中 被复制的数字 是 m,则把它复制到辅助数组中下标为 m 的位置

时间复杂度为 O ( n ) O(n) O(n) ,空间复杂度为 O ( n ) O(n) O(n)

代码示例:

int repeatNum(vector<int>& v){
    int len = v.size();
    vector<int> v1(len);
    for(int i = 0; i < len; ++i){
        if(v1[v[i]]) return v1[v[i]];
        else v1[v[i]] = v[i];
    }
    return -1;
}

2.3 方法三:二分查找

将 1 ~ n 的数字从中间的数字 分成两部分,即分成 1 ~ m m+1 ~ n

  • 若 1 ~ m 的数字,在整个数组上的数目超过 m,即超过该区间的长度,那么这一半的区间里一定包含重复的数字
  • 否则,另一半 m+1 ~ n 区间里一定包含重复的数字

继续把包含重复数字的区间一分为二,直到找到一个重复的数字

时间复杂度为 O ( n l o g n ) O(nlog_n) O(nlogn​),空间复杂度为 O ( 1 ) O(1) O(1)

代码示例:

int countRange(vector<int>& v, int sz, int start, int end){
    if(v.empty()) return 0;

    int count = 0;
    for(int i = 0; i < sz; ++i){
        if(v[i] >= start && v[i] <= end){
            ++count;
        }
    }
    return count;
}

int getrepeat(vector<int>& v){
    if(v.empty()) return -1;
    
    int sz = v.size();
    int start = 1, end = sz-1;
    while(end >= start){
        int mid = start + ((end-start)>>1);
        int count = countRange(v, sz, start, mid);
        if(end == start){
            if(count > 1) return start;
            else break;
        }
        if(count > (mid - start + 1)) end = mid;
        else start = mid + 1;
    }
    return -1;
}

测试代码:

bool duplicate(vector<int>& v, int **res){
    if(v.empty()) return false;
    for(int i = 0; i < v.size(); ++i){
        if(v[i] < 0 || v[i] > v.size()-1) 
            return false;
    }
    
    for(int i = 0; i < v.size(); ++i){
        while(v[i] != i){
            if(v[i] == v[v[i]]) {
                *res = &v[i];
                return true;
            }
            swap(v[i], v[v[i]]);
        }
    }
    return false;
}

int main(){
    int arr[] = {2, 3, 5, 4, 3, 2, 6, 7};
    vector<int> v(arr, arr+8);  // 这种赋值方式不会导致vector自动扩展内部大小

    int* res = nullptr;
    if(duplicate(v, &res)) cout << *res << endl;
    else cout << '0' << endl;
    //cout << repeatNum(v) << endl;
    /*
    for(int i = 0; i < v.size(); i++){
        if(i == 0) cout << v[i];
        else cout << ' ' << v[i];
    }
    cout << endl;
    
    for(auto a : v){    // 有两个警告(auto是C++_11的扩展)
        cout << a << ' ';
    }
    */
    return 0;
}

到此这篇关于关于C++数组中重复的数字的文章就介绍到这了,更多相关C++数组中重复数字内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

注:文章转自微信众号:Coder梁(ID:Coder_LT)

相关文章

  • C++驱动bash的实现代码

    C++驱动bash的实现代码

    这篇文章主要介绍了C++驱动bash的实现代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • 盘点分析C语言中少见却强大的字符串函数

    盘点分析C语言中少见却强大的字符串函数

    这篇文章主要为大家盘点及分析C语言中少见却强大的字符串函数,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • C语言二维数组应用实现扫雷游戏

    C语言二维数组应用实现扫雷游戏

    这篇文章主要为大家详细介绍了C语言二维数组应用实现扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C++获取文件大小数值的三种方式介绍

    C++获取文件大小数值的三种方式介绍

    最近在做项目时经常需要获得文件的大小操作,虽然在网络上已经有许多篇博客介绍了,但是还是想总结出自己一篇,记录一下自己在项目中是怎么获得文件大小的
    2022-10-10
  • 顺序线性表的代码实现方法

    顺序线性表的代码实现方法

    下面小编就为大家带来一篇顺序线性表的代码实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • C++深入详解单例模式与特殊类设计的实现

    C++深入详解单例模式与特殊类设计的实现

    这篇文章主要为大家详细介绍了C++单例模式和特殊类的设计,单例模式这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-06-06
  • C语言深入探究sizeof与整型数据存储及数据类型取值范围

    C语言深入探究sizeof与整型数据存储及数据类型取值范围

    在main函数中,sizeof是可以正常工作的,但是在自定义函数中就不可以了。所以本文将为大家详细讲解一下关键字sizeof、整型数据存储深入、数据类型取值范围深入
    2022-07-07
  • C语言读取BMP图像数据的源码

    C语言读取BMP图像数据的源码

    这篇文章主要介绍了C语言读取BMP图像数据的源码,需要的朋友可以参考下
    2013-03-03
  • C++ 中私有继承的作用

    C++ 中私有继承的作用

    这篇文章主要介绍了C++ 中私有继承的作用的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-10-10
  • C语言中多维数组的内存分配和释放(malloc与free)的方法

    C语言中多维数组的内存分配和释放(malloc与free)的方法

    写代码的时候会碰到多维数组的内存分配和释放问题,在分配和释放过程中很容易出现错误。下面贴上一些示例代码,以供参考。
    2013-05-05

最新评论