【数组中重复的数字】-C语言-题解
原题链接:数组中重复的数字
一、描述:
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
二、样例:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:
2 或 3
三、数据范围:
2 <= n <= 100000
四、题解
1. 利用下标索引
即通过创建额外的数组空间用来记录各个数出现的次数,具体实现方法是:
(1)创建数组,并将数组元素初始化为0
(2)遍历原数组,以出现的数作为用于计数的数组的下标,该下标所对应的计数数组中的元素++
(3)计数数组中的元素若大于1,则该元素所对应的下标就是原数组中那个重复的数字。
以样例为例:
设计数数组为count[100001]
,则遍历原数组后可得到:
count[0] == 1;
count[1] == 1;
count[2] == 2;
count[3] == 2;
count[5] == 1;
故2或3就是原数组中重复的数
- 完整代码:
int findRepeatNumber(int* nums, int numsSize)
{int hash[100001] = {0};int i = 0;for(i = 0; i < numsSize; i++){if(++hash[nums[i]] > 1)return nums[i];}return 0;
}
复杂度分析:
使用了额外的空间,空间复杂度O(n);
遍历整个数组,时间复杂度O(n)。
2. 原地置换(重点学习)
顾名思义,算法的主要思想就是在原数组的基础上根据不同索引值进行数据的置换,然后根据置换的结果,就可以判断出那个重复的数,下面详细介绍。
介绍之前,先总结列出文中相关句段所对应的代码段,可能会便于理解一些:
设循环遍量为 i ,则:
当前元素或者说以 i 为下标索引得到当前元素表示为:nums[i]
以循环变量 i 作为直接索引得到当前元素表示为:i == nums[i]
以当前元素本身 作为数组下标索引得到当前元素表示为: nums[nums[i]] == nums[i]
这里再规定一下 “正确的位置” 的含义以便说明:
“正确的位置” 代表该元素正好就是以该元素本身为数组下标索引所得的元素。如:数组arr[5] = {0, 2, 1, 3, 6}
,其中的元素0与元素3就处于 “正确的位置” ,因为arr[0] == 0
,arr[3] == 3
。
(1)算法思想:遍历数组,每遇到一个元素就检查该元素是否位于正确的位置上:
- 先以循环变量
i
作为直接索引进行检查,即if(i == nums[i])
,若符合条件则说明,此时元素恰好位于正确的位置上(原来在数组中就处于正确位置或交换后处于正确位置),那么就执行i++
跳过而检查下一个元素; - 再以该元素本身作为下标索引进行检查,即
if(nums[nums[i]] == nums[i])
,若符合条件则说明,此元素在之前已经出现过,出现时的位置要么恰好是正确的位置,要么是经过交换来到了正确的位置,因此该元素就为重复元素; - 若都不符合两种检查方式的条件,则说明元素不在正确的位置上,那么需与相对当前元素来说的正确位置上的元素进行交换,从而让该元素元素处于正确位置;但需要注意的是,交换可以让当前元素处于正确的位置上,但不能保证被交换的元素处于正确的位置上,所以在执行完交换后
i
不进行++
,直到使两个交换的元素都处于正确的位置上后,才由条件if(i == nums[i])
来控制i的++。(后面有例)
由如上描述可知,符合第一种情况的一定符合第二种情况,即:若有i == nums[i]
,则必有i == nums[i] == nums[nums[i]]
;反之则不一定成立,这个不一定成立的情况,也正是重复元素的情况,即有 nums[i] == nums[nums[i]]
,但同时i != nums[i]
总结来说就是:利用原地置换处理的数组,对于数组中的不重复的元素必有i == nums[i]
; 当不满足这个条件,且满足 nums[i] == nums[nums[i]]
条件时,nums[i]
就是重复的元素
(2)样例说明:对于数组 [2, 3, 1, 0, 2, 5, 3],原地置换的过程如下:
- 当
i = 0
时,当前元素nums[i] = 2
, 元素 2 不在正确的位置上,且以该元素本身为下标索引找到的元素 1 不等于当前元素,所以将其与以2为下标的数组元素进行交换,交换结果为: [1, 3, 2, 0, 2, 5, 3]; - 交换完后发现当前元素
nums[i] = 1
,元素 1 不在正确的位置上,且以该元素本身为下标索引找到的元素 3 不等于当前元素执行交换,交换结果为:[3, 1, 2, 0, 2, 5, 3] - 交换完后发现当前元素
nums[i] = 3
,元素 3 不在正确的位置上,且以该元素本身为下标索引找到的元素 0 不等于当前元素,执行交换,交换结果为:[0, 1, 2, 3, 2, 5, 3] - 交换完后发现当前元素位于正确位置,
i++
跳过,进行下一个数的检查,由此,i一直++到4; - 当
i = 4
时,当前元素nums[i] = 2
不在正确的位置上,但以该元素本身为下标索引能到的元素 2 等于当前元素,说明当前元素为重复元素,返回当前元素 2 - (3)形象理解:
此形象理解来源于原题题解下的评论,这里转述一下,供大家参考:
这个原地交换法就相当于分配工作,每个索引代表一个工作岗位,每个岗位必须专业对口,即0索引必须是0元素的“专业人才”才能上岗。而我们的目的就是通过专业对口的方式找出溢出的人才。我们先从0索引岗位开始遍历,首先我们看0索引岗位是不是已经专业对口了,如果已经专业对口即nums[0]=0,那我们就跳过0岗位看1岗位。如果0索引没有专业对口,那么我们看现在0索引上的人才调整到他对应的岗位上,比如num[0]=2,那我们就把2这个元素挪到他对应的岗位num[2]上,这个时候有两种情况:1、num[2]岗位上已经有专业对口的人才了,既num[2]=2,这就说明刚刚那个在num[0]上的2是溢出的人才,我们直接将其返回即可。2、num[2]上的不是专业对口的人才,那我们将num[0]上的元素和num[2]上的元素交换,这样num[2]就找到专业对口的人才了。之后重复这个过程直到帮num[0]找到专业对口的人才,然后以此类推帮num[1]找人才、帮num[2]找人才,直到找到溢出的人才。
(4)算法实现注意事项:
- 检查的次序问题:如上所述,应先检查
if(i == nums[i])
,再检查if(nums[nums[i]] == nums[i])
- 交换注意问题:这里用一个错误的代码作为例子,请看:
tmp = nums[i];nums[i] = nums[nums[i]];nums[nums[i]] = tmp;
这样的交换存在问题:在执行完第二句语句时,nums[i]
的值已被改为nums[nums[i]]
,故在第三句语句用nums[i]
来作为数组下标索引时就达不到我们预期的效果,最后一句语句我们想的把nums[i]
的值赋给nums[nums[i]]
,而如上代码相当于是把nums[i]
的值赋给了nums[nums[nums[i]]]
,这就出现问题了。
正确更改方式为,把最后一句语句中的nums[i]
作为下标索引改为tmp
作为下标索引,即:nums[tmp] = tmp;
复杂度分析:
使用了常数复杂度的空间,空间复杂度O(1);
遍历整个数组,时间复杂度O(n)。
- 完整代码:
int findRepeatNumber(int* nums, int numsSize)
{int i = 0;int tmp = 0;while(i < numsSize){if(i == nums[i]){i++;continue;}if(nums[i] == nums[nums[i]])return nums[i];if(i != nums[i]){tmp = nums[i];nums[i] = nums[nums[i]];nums[tmp] = tmp;//nums[i]的值已变,故要用tmp索引}}return -1; //没有重复的元素返回-1
}
看完觉得有觉得帮助的话不妨点赞收藏鼓励一下,有疑问或看不懂的地方或有可优化的部分还恳请朋友们留个评论,多多指点,谢谢朋友们!🌹🌹🌹
相关文章:
【数组中重复的数字】-C语言-题解
原题链接:数组中重复的数字 一、描述: 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数…...

C++调用Python脚本进行18次循环操作后,脚本不执行
C调用Python脚本进行18次循环操作后,脚本不执行 现象: 发送端接收端 从第二张图中可以看出,python脚本卡在’[parkin_debug] 6’与’[parkin_debug] 7’之间 该测试经过多次反复测试,均在第18次循环执行时,出现上述问…...

字节10年架构师职业发展经历,助你做好职业规划
一直以来程序员这一职业都给人高薪资的印象,近年来随着互联网行业的快速发展,程序员更是人满为患,然而很多人关注的却是程序员的薪资,而非职业本身。 一批批程序员进入工作岗位,但是很多人并没有对自己的职业生涯有清…...
ArrayList真的是因为实现了RandomAccess接口才能做到快速随机访问的吗
ArrayList和RandomAccess接口RandomAccess 接口Collections.binarySearch()源码总结RandomAccess 接口 首先,RandomAccess接口是什么,以下代码可见: public interface RandomAccess { }RandomAccess接口其实是一个标记接口,它只…...

OSI七层模型与物理层与设备链路层
目录 协议 举例 OSI七层模型 理解七层模型 以下为OSI七层模型数据逐层封装和数据逐层解封的过程 TCP/IP参考模型 数据包的层层封装与层层拆包 各层的数据以及协议 封装所用的协议的数字表示形式 物理层 模拟信号 模拟信号特点 数字信号 数字信号特点 数据通信模…...

Java8的Optional类的使用 和 Stream流式操作
Java知识点总结:想看的可以从这里进入 目录13.6、Optional类13.7、Stream13.7.1、Stream创建13.7.2、中间操作1、筛选2、切片3、映射4、排序13.7.3、终止操作1、遍历2、聚合3、匹配查找4、归约5、收集归集统计分组字符串拼接归约13.6、Optional类 Optional类是一个…...

Authorization Server 认证服务
Hi Auth HiAuth是一个开源的基于Oauth2协议的认证、授权系统,除了标准的Oauth2授权流程功能外,还提供了应用管理、用户管理、权限管理等相关功能。 在这个项目中你能够了解到如何基于spring-security-oauth2-authorization-server实现自己的Authorizat…...
研制过程评审活动(五)生产定型阶段
1. 生产定型阶段主要任务 生产定型的主要任务是对产品批量生产条件和质量稳定情况进行全面考核,以确认产品是否达到批量生产的标准。 需要生产定型的军工产品,在完成设计定型并经小批量试生产后、正式批量生产之前,进行生产定型。生产定型的条件和时间,由定委在批准设计…...
NCUT加权的NMF
矩阵定义 X:特征矩阵,矩阵的维度为体素数mx(指标数x被试数)n S:相似性矩阵,由特征矩阵的每一行计算皮尔逊相关得到mxm的方阵 D:度矩阵,度矩阵的对角线元素由相似性矩阵S对应的行和…...

从0开始的ios自动化测试
最近由于工作内容调整,需要开始弄ios自动化了。网上信息有点杂乱,这边我就按我的实际情况,顺便记录下来,看是否能帮到有需要的人。 环境准备 安装tidevice pip3 install -U “tidevice[openssl]” 它的作用是,帮你绕…...

vue3中使用jszip压缩文件
1、安装依赖 npm install jszip npm install file-saver --save 2、使用 <template><el-card class"mb15"><template #header><span>jszip</span></template><!-- 二维码容器 --><div id"qrCodeBox">&…...

React 虚拟DOM的前世今生
引文 通过本文你将了解到 什么是虚拟DOM?虚拟DOM有什么优势?React的虚拟Dom是如何实现的?React是如何将虚拟Dom转变为真实Dom? 一、前言 要了解虚拟DOM,我们先明确一下DOM的概念。 根据MDN的说法: 文档…...
Java环境变量配置
一、Path环境变量配置设置环境变量的值:C:\Program Files\Java\jdk-17\bin目前较新的JDK安装时会自动配置javac、java程序的路径到Path环境变量中去 ,因此,javac、java可以直接使用。注意:以前的老版本的JDK在安装的是没有自动配置…...

超详细解读!数据库表分区技术全攻略
更多内容可以关注微信公众号:老程序员刘飞 分区的定义 分区是一种数据库优化技术,它可以将大表按照一定的规则分成多个小表,从而提高查询和维护的效率。在分区的过程中,数据库会将数据按照分区规则分配到不同的分区中࿰…...

Redis高可用集群方案
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 @[TOC](文章目录)主从复制哨兵模式(sentinel)Cluster集群在生产过程中,Redis不一定会单独部署。因为一旦redis服务因为某些原因导致无法提供数,那么redis就不可用了。那么实现redis高可用的方式就…...

企业微信机器人发送消息
前言 随着科技的发展,各企业公司的业务不断发展,那么就需要强有力的沟通软件,其中企业微信、钉钉的能力得到了大众的认可,今天这篇文章就讲其中的一个功能-调用企业微信机器人(下文简称应用)进行消息传递。它的好处有哪些呢?自然是可以让相关人员及时追踪任务进度。 一、…...

使用PHP+yii2调用asmx服务接口
一.创建服务端 1:创建一个ASP.NET web应用程序 2:选择空的模板 3:系统生成项目目录 4:右键项目-添加项-新建项 5:选择Web 服务(ASMX) 6:选择之后项目中会有一个Test.asmx服务程序,…...
【042】904. 水果成篮[滑动窗口]
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果&…...
Linux基础知识(一)
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...
Redis面试题
目录 Redis持久化机制:RDB和AOF Redis线程模型有哪些?单线程为什么快? Redis的过期键有哪些删除策略? Redis集群方案有哪些? redis事务怎么实现? 为什么redis不支持回滚? redis主从复制的原理是什么 …...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...