关于C++数组中重复的数字
作者:zx255 发布时间:2023-01-21 03:29:43
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(log2n)
代码示例:
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;
}
注:文章转自微信公众号:Coder梁(ID:Coder_LT)
来源:https://juejin.cn/post/7025798755139977247


猜你喜欢
- 导言目前截屏的方法很多,root不适用,要么其他方法就是有局限性,而其中官方给出的方案最好—MediaProjection介绍Android
- 在使用C#进行相关编程的时候,有时候我们需要获取系统相关的进程信息。那么在C#中如何获取系统的所有进程那?下面请跟小编一起来操作。1、首先新
- 在上一节中,我带大家学习了详解SpringBoot集成Redis来实现缓存技术方案,尤其是结合Spring Cache的注解的实现方案,接下
- 效果:说明:获取本局域网的MAC地址(非本机的MAC地址)代码:/// <summary>
- /* * 使用 C# 动态编译代码和执行 * 作者: yaob */ static void Main(string[] args) { /
- 前言Java8 的新特性:Lambda表达式、强大的 Stream API、全新时间日期 API、ConcurrentHashMap、Met
- 去年就已经学了这个技术了,一直没去写,现在抽个时间写了个俄罗斯方块游戏。只有简单的新游戏,暂停,继续,积分功能。简单的实现了俄罗斯的经典功能
- 1.概述在本快速教程中,我们将学习如何设置Spring Security LDAP。在我们开始之前,了解一下LDAP是什么? - 它代表轻量
- 方法1 :利用Struts 2的支持的可配置结果,可以达到过滤器的效果。Action的处理结果配置支持正则表达式。但是如果返回的对象是一个数
- 用c#中创建一个windows服务非常简单,与windows服务相关的类都在System.ServiceProcess命名空间下。每个服务都
- 本文实例为大家分享了Android系统工具类的具体代码,供大家参考,具体内容如下系统工具类public class systemUtil {
- 最近项目中用到了Elasticsearch5.4(ES)是比较新的一个版本,使用的过程中出现了很多的问题,很是头疼,但是问题最终还是解决掉了
- 从大学就开始做C#这块,也做C#几年了,最近又从ios转回.Net,继续做C#,之前也没有写博客的习惯,写博客也是从我做ios的时候开始的,
- 前文传送门:NioSocketChannel注册到selector我们回到AbstractUnsafe的register0()方法:priv
- 本篇内容主要讲述了实现基于微软账户的第三方身份验证、实现双因子身份验证、 验证码机制这3个内容。实现基于微软账户的第三方身份验证在
- 在上一节,我们已经完成了项目的整体技术架构设计和具体的数据库设计,接下来,我们搭建整体的开发框架。开发工具选用Idea。开发工具只是为了提高
- 实现客户端发送请求,服务器端响应机制UDP客户端代码using System;using System.Text;using System.
- 基于 kotlin/coroutine/retrofit/jetpack 打造,100来行代码,用法超级简单舒适设置默认Retrofit工厂
- 使用idea的file-》settings-》plugins安装maven helper插件失败,安装页面总是提示installed,在in
- 效果图在开发APP中,经常要实现圆形头像,那么该如何实现呢?要裁剪吗,要重写draw函数吗?不用,只用一行代码就可以实现Glide实现圆形图