位运算题目:寻找重复数

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
      • 进阶
  • 前言
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法三
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:寻找重复数

出处:287. 寻找重复数

难度

6 级

题目描述

要求

给定一个包含 n + 1 \texttt{n} + \texttt{1} n+1 个整数的数组 nums \texttt{nums} nums,每个整数都在范围 [1, n] \texttt{[1, n]} [1, n] 内(包括 1 \texttt{1} 1 n \texttt{n} n)。

数组 nums \texttt{nums} nums 只有一个重复的整数,返回这个重复的数。

要求不修改数组 nums \texttt{nums} nums 且只用常量级的额外空间。

示例

示例 1:

输入: nums = [1,3,4,2,2] \texttt{nums = [1,3,4,2,2]} nums = [1,3,4,2,2]
输出: 2 \texttt{2} 2

示例 2:

输入: nums = [3,1,3,4,2] \texttt{nums = [3,1,3,4,2]} nums = [3,1,3,4,2]
输出: 3 \texttt{3} 3

示例 3:

输入: nums = [3,3,3,3,3] \texttt{nums = [3,3,3,3,3]} nums = [3,3,3,3,3]
输出: 3 \texttt{3} 3

数据范围

  • 1 ≤ n ≤ 10 5 \texttt{1} \le \texttt{n} \le \texttt{10}^\texttt{5} 1n105
  • nums.length = n + 1 \texttt{nums.length} = \texttt{n} + \texttt{1} nums.length=n+1
  • 1 ≤ nums[i] ≤ n \texttt{1} \le \texttt{nums[i]} \le \texttt{n} 1nums[i]n
  • nums \texttt{nums} nums 中的所有整数都只出现一次,除了一个整数出现两次或多次

进阶

  • 如何证明 nums \texttt{nums} nums 中至少存在一个重复的数字?
  • 你可以设计一个线性级时间复杂度的解决方案吗?

前言

由于数组的长度是 n + 1 n + 1 n+1,且最多包含 n n n 个不同的整数。根据抽屉原理(或鸽笼原理)可知,将数组的 n + 1 n + 1 n+1 个位置分配到 n n n 个不同的整数,至少有两个位置分配到同一个整数,即该整数在数组中重复。因此数组中至少存在一个重复的数字。

由于这道题要求不修改数组且空间复杂度是 O ( 1 ) O(1) O(1),因此排序、哈希表等解法都是不允许的,需要使用其他解法寻找重复的数字。

解法一

思路和算法

每个正整数的二进制表示中至少有一位是 1 1 1。对于二进制表示的每一位,分别考虑数组 nums \textit{nums} nums 中的所有整数在该位的 1 1 1 的次数 countArr \textit{countArr} countArr 和范围 [ 1 , n ] [1, n] [1,n] 中的所有整数在该位的 1 1 1 的次数 countNum \textit{countNum} countNum。假设整数 x x x 在数组 nums \textit{nums} nums 中出现超过一次,则 x x x 的二进制表示的每个 1 1 1 所在的位都满足 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum。理由如下。

  • 如果 x x x 出现两次,则范围 [ 1 , n ] [1, n] [1,n] 中的所有整数都在数组 nums \textit{nums} nums 中出现,因此对于 x x x 中等于 1 1 1 的每一位都有 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum

  • 如果 x x x 出现超过两次,则范围 [ 1 , n ] [1, n] [1,n] 中的部分整数不在数组 nums \textit{nums} nums 中出现,可以看成 x x x 替代了这部分整数。对于 x x x 中等于 1 1 1 的每一位,被替代的整数在相应位的值等于 0 0 0 1 1 1。替代之前有 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum,替代之后同样有 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum

除了 x x x 的二进制表示的每个 1 1 1 所在的位以外,其余位都不满足 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum。因此上述结论的逆命题也成立:假设所有满足 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum 的位组成的整数是 x x x,则 x x x 在数组 nums \textit{nums} nums 中出现超过一次。

根据上述分析,可以使用位运算寻找重复数。首先找到 n n n 的二进制表示的最高有效位,即最高位 1 1 1 所在的位数,然后依次遍历最低有效位到最高有效位,对于每一位计算 countArr \textit{countArr} countArr countNum \textit{countNum} countNum,如果 countArr > countNum \textit{countArr} > \textit{countNum} countArr>countNum 则将该位加到重复数中。遍历结束之后即可得到重复数。

代码

class Solution {public int findDuplicate(int[] nums) {int n = nums.length - 1;int highBit = 0;int temp = n;while (temp != 0) {highBit = temp & (-temp);temp -= highBit;}int duplicate = 0;for (int i = 1; i <= highBit; i <<= 1) {int countArr = 0, countNum = 0;for (int num : nums) {int bit = num & i;if (bit != 0) {countArr++;}}for (int j = 1; j <= n; j++) {int bit = j & i;if (bit != 0) {countNum++;}}if (countArr > countNum) {duplicate += i;}}return duplicate;}
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 nums \textit{nums} nums 的长度减 1 1 1。需要遍历的二进制表示位数是 O ( log ⁡ n ) O(\log n) O(logn),对于每一位都需要 O ( n ) O(n) O(n) 的时间计算该位在重复数中是否等于 1 1 1,时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( 1 ) O(1) O(1)

解法二

思路和算法

假设有一个长度为 n + 1 n + 1 n+1 的数组 counts \textit{counts} counts,其中 counts [ i ] \textit{counts}[i] counts[i] 表示数组 nums \textit{nums} nums 中的不超过 i i i 的整数个数。以下用 x x x 表示重复数。

如果 x x x 出现两次,则当 i < x i < x i<x counts [ i ] = i \textit{counts}[i] = i counts[i]=i,当 i ≥ x i \ge x ix counts [ i ] = i + 1 > i \textit{counts}[i] = i + 1 > i counts[i]=i+1>i

如果 x x x 出现超过两次,则范围 [ 1 , n ] [1, n] [1,n] 中的部分整数不在数组 nums \textit{nums} nums 中出现,可以看成 x x x 替代了这部分整数,假设只有一个整数被替代,用 j j j 表示被替代的整数,分别考虑 j < x j < x j<x j > x j > x j>x 的情况。

  • 如果 j < x j < x j<x,则对于 i < x i < x i<x,不超过 i i i 的整数个数不变或减少,因此 counts [ i ] ≤ i \textit{counts}[i] \le i counts[i]i,对于 i ≥ x i \ge x ix,不超过 i i i 的整数个数不变,因此 counts [ i ] > i \textit{counts}[i] > i counts[i]>i

  • 如果 j > x j > x j>x,则对于 i < x i < x i<x,不超过 i i i 的整数个数不变,因此 counts [ i ] = i \textit{counts}[i] = i counts[i]=i,对于 i ≥ x i \ge x ix,不超过 i i i 的整数个数不变或增加,因此 counts [ i ] > i \textit{counts}[i] > i counts[i]>i

因此当 i < x i < x i<x counts [ i ] ≤ i \textit{counts}[i] \le i counts[i]i,当 i ≥ x i \ge x ix counts [ i ] > i \textit{counts}[i] > i counts[i]>i。如果被替代的整数有多个,该结论仍成立。

重复数 x x x 是使得 counts [ x ] > x \textit{counts}[x] > x counts[x]>x 成立的最小整数 x x x,可以使用二分查找的方法寻找重复数。

low \textit{low} low high \textit{high} high 分别表示二分查找的下界和上界。由于 x x x 在范围 [ 1 , n ] [1, n] [1,n] 中,因此初始时 low = 1 \textit{low} = 1 low=1 high = n \textit{high} = n high=n

每次查找时,取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,计算数组 nums \textit{nums} nums 中的不超过 mid \textit{mid} mid 的整数个数 counts [ mid ] \textit{counts}[\textit{mid}] counts[mid],执行如下操作。

  • 如果 counts [ mid ] ≤ mid \textit{counts}[\textit{mid}] \le \textit{mid} counts[mid]mid,则重复数 x x x 小于等于 mid \textit{mid} mid,因此在 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中继续查找。

  • 如果 counts [ mid ] > mid \textit{counts}[\textit{mid}] > \textit{mid} counts[mid]>mid,则重复数 x x x 大于 mid \textit{mid} mid,因此在 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中继续查找。

low = high \textit{low} = \textit{high} low=high 时,查找结束,此时 low \textit{low} low 即为重复数 x x x

实现方面,并不需要显性创建数组 counts \textit{counts} counts,而是可以在二分查找的过程中根据当前的 mid \textit{mid} mid 遍历数组 nums \textit{nums} nums 计算不超过 mid \textit{mid} mid 的整数个数,因此空间复杂度是 O ( 1 ) O(1) O(1)

代码

class Solution {public int findDuplicate(int[] nums) {int n = nums.length - 1;int low = 1, high = n;while (low < high) {int mid = low + (high - low) / 2;int count = 0;for (int num : nums) {if (num <= mid) {count++;}}if (count > mid) {high = mid;} else {low = mid + 1;}}return low;}
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 nums \textit{nums} nums 的长度减 1 1 1。需要执行 O ( log ⁡ n ) O(\log n) O(logn) 次二分查找,每次二分查找需要 O ( n ) O(n) O(n) 的时间遍历数组 nums \textit{nums} nums 计算不超过特定值的整数个数,时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( 1 ) O(1) O(1)

解法三

思路和算法

将数组看成有向图,范围 [ 0 , n ] [0, n] [0,n] 中的每个整数是一个结点,对于 0 ≤ i ≤ n 0 \le i \le n 0in 的每个下标 i i i,存在一条从 i i i 指向 nums [ i ] \textit{nums}[i] nums[i] 的有向边。对于重复数 x x x,存在至少两条指向 x x x 的边,因此有向图中存在环。

这道题可以看成在有向图中寻找环的入口,可以使用「环形链表 II」的快慢指针做法。以下只说明使用快慢指针寻找重复数的做法,正确性证明见「环形链表 II 的题解」。

寻找重复数分成两步。

第一步是使用快慢指针遍历有向图寻找相遇点。

初始时,快指针和慢指针都位于整数 0 0 0。每次将快指针移动两步,慢指针移动一步,在至少移动一次的情况下,当快指针和慢指针相遇时,相遇的位置为相遇点。

第二步是将两个指针分别从起点和相遇点开始遍历有向图寻找重复数。

初始时,两个指针分别位于整数 0 0 0 和相遇点。每次将两个指针各移动一步,两个指针相遇的整数即为重复数。

代码

class Solution {public int findDuplicate(int[] nums) {int fast = 0, slow = 0;int meet = -1;while (meet < 0) {fast = nums[nums[fast]];slow = nums[slow];if (fast == slow) {meet = fast;}}int pointer1 = 0, pointer2 = meet;while (pointer1 != pointer2) {pointer1 = nums[pointer1];pointer2 = nums[pointer2];}return pointer1;}
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度减 1 1 1。快慢指针的时间复杂度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( 1 ) O(1) O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch:没有 “AG” 的 RAG?

作者&#xff1a;来自 Elastic Gustavo Llermaly 了解如何利用语义搜索和 ELSER 构建一个强大且视觉上吸引人的问答体验&#xff0c;而无需使用 LLMs。 想要获得 Elastic 认证&#xff1f;查看下一期 Elasticsearch Engineer 培训的时间&#xff01; Elasticsearch 拥有众多新…

linux下安装ollama网不好怎么办?

文章目录 前言kkgithub下载脚本,而不是直接运行修改脚本修改权限还是不行?前言 今天想在linux上面更新一下ollama,于是去到官网: https://ollama.com/download/linux linux下安装ollama还是挺简单的: curl -fsSL https://ollama.com/install.sh | sh我也是特别嗨皮地就…

相机-IMU联合标定:相机-IMU外参标定

文章目录 📚简介🚀标定工具kalibr🚀标定数据录制🚀相机-IMU外参标定📚简介 在 VINS(视觉惯性导航系统) 中,相机-IMU外参标定 是确保多传感器数据时空统一的核心环节,其作用可概括为以下关键点: 坐标系对齐(空间同步),外参误差会导致视觉特征点投影与IMU预积…

基于 Java 的实现前端组装查询语句,后端直接执行查询方案,涵盖前端和后端的设计思路

1. 前端设计 前端负责根据用户输入或交互条件,动态生成查询参数,并通过 HTTP 请求发送到后端。 前端逻辑: 提供用户界面(如表单、筛选器等),让用户选择查询条件。将用户选择的条件组装成 JSON 格式的查询参数。发送 HTTP 请求(如 POST 或 GET)到后端。示例: 假设用…

[STM32] 4-2 USART与串口通信(2)

文章目录 前言4-2 USART与串口通信(2)数据发送过程双缓冲与连续发送数据发送过程中的问题 数据接收过程TXE标志位&#xff08;发送数据寄存器空&#xff09;TC标志位&#xff08;发送完成标志位&#xff09;单个数据的发送数据的连续发送 接收过程中遇到的问题问题描述&#xf…

Qt多线程TCP服务器实现指南

在Qt中实现多线程TCP服务器可以通过为每个客户端连接分配独立的线程来处理&#xff0c;以提高并发性能。以下是一个分步实现的示例&#xff1a; 1. 自定义工作线程类&#xff08;处理客户端通信&#xff09; // workerthread.h #include <QObject> #include <QTcpSo…

详细介绍Python-pandas-DataFrame全部 *功能* 函数

Python-pandas-DataFrame全部 功能 函数 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是pandas的使用语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1a;每个知识点…

香港科技大学广州|可持续能源与环境学域博士招生宣讲会—四川大学专场

香港科技大学广州&#xff5c;可持续能源与环境学域博士招生宣讲会—四川大学专场 时间&#xff1a;2025年5月8日&#xff08;星期四&#xff09;16:30开始 地点&#xff1a;四川大学基础教学楼A座504 宣讲嘉宾&#xff1a;肖殿勋 助理教授 一经录取&#xff0c;享全额奖学金…

装饰器设计模式(Decorator Pattern)详解

装饰器设计模式(Decorator Pattern)详解 装饰器模式是一种结构型设计模式,它允许动态地向对象添加额外行为,而无需修改其原始类。这种模式通过包装对象的方式提供灵活的扩展功能替代继承。 1. 核心概念 (1)模式定义 装饰器模式:动态地给一个对象添加一些额外的职责,就…

【SpringMVC】详解参数传递与实战指南

目录 1.前言 2.正文 2.1基础参数传递 2.1.1单参数 2.1.2多参数 2.2对象参数绑定 2.2.1自动封装对象 2.2.2参数别名处理 2.3集合类型处理 2.3.1数组接收 2.3.2List集合接收 2.4JSON参数处理 2.4.1介绍JSON 2.4.2传递JSON参数 2.5RESTful风格参数 2.6文件上传处理…

mysql-窗口函数一

目录 一、感受一下分组与窗口函数的区别 二、滑动窗口&#xff08;子窗口&#xff09;大小的确认 2.1 分组函数下order by使用 2.2 窗口子句 2.3 执行流程 三、函数使用 窗口函数需要mysql的版本大于等于8才行&#xff0c;可以先检查一下自己的mysql版本是多少 select ve…

解决在Mac上无法使用“ll”命令

在 macOS 上&#xff0c;ll 命令是一个常见的别名&#xff0c;它通常是指向 ls -l 的。但是&#xff0c;如果你看到 zsh: command not found: ll&#xff0c;这意味着你当前的 zsh 配置中没有设置 ll 作为别名。 解决方法&#xff1a; 1. 使用 ls -l 命令 如果只是想查看目录…

GTA5(传承/增强) 13980+真车 超跑 大型载具MOD整合包+最新GTA6大型地图MOD 5月最新更新

1500超跑载具 1000普通超跑 1500真车超跑 各种军载具1000 各种普通跑车 船舶 飞机 1000 人物1500 添加式led载具1000 超级英雄最新版 添加添加式武器MOD1000 添加地图MOD500 添加超跑载具2000 当前共计1.2wMOD 4月2日更新 新增770menyoo地图 当前共计12770 新增48款超级英雄最新…

初学Vue之记事本案例

初学Vue之记事本案例 案例功能需求相关Vue知识案例实现1.实现方法及代码2.演示 案例收获与总结 案例功能需求 基于Vue实现记事功能&#xff08;不通过原生JS实现&#xff09; 1.点击保存按钮将文本框的内容显示在特定位置&#xff0c;且清空文本框内容 2.点击清空按钮&#x…

一个linux系统电脑,一个windows电脑,怎么实现某一个文件夹共享

下载Samba linux主机名字不能超过15个字符 sudo dnf install samba samba-client -y 创建共享文件夹 sudo mkdir /shared 配置文件 vim /etc/samba/smb.conf [shared] path /shared available yes valid users linux电脑用户 read only no browsable yes p…

树莓派5+edge-tts 语音合成并进行播放测试

简介 Edge-TTS 是一个基于微软 Edge 浏览器的开源文本转语音(TTS)工具,主要用于将文本转换为自然流畅的语音。它利用了微软 Azure 的 TTS 技术,支持多种语言和声音,同时具备高质量的语音合成能力。这里简单演示在树莓派中安装该项目进行简单测试。 开源仓库地址:https:/…

多模态革命!拆解夸克AI相机技术架构:如何用视觉搜索重构信息交互?(附开源方案对比)

一、技术人必看&#xff1a;视觉搜索背后的多模态架构设计 夸克「拍照问夸克」功能绝非简单的OCRQA拼接&#xff0c;而是一套多模态感知-推理-生成全链路系统&#xff0c;其技术栈值得开发者深挖&#xff1a; 视觉编码器&#xff1a;基于Swin Transformer V2&#xff0c;支持4…

论文阅读:2024 ICLR Workshop. A STRONGREJECT for Empty Jailbreaks

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 A STRONGREJECT for Empty Jailbreaks 对空越狱的 StrongREJECT https://arxiv.org/pdf/2402.10260 https://github.com/dsbowen/strong_reject https://strong-reject.re…

AI生成Flutter UI代码实践(一)

之前的杂谈中有提到目前的一些主流AI编程工具&#xff0c;比如Cursor&#xff0c;Copilot&#xff0c;Trea等。因为我是Android 开发&#xff0c;日常使用Android Studio&#xff0c;所以日常使用最多的还是Copilot&#xff0c;毕竟Github月月送我会员&#xff0c;白嫖还是挺香…

计网分层体系结构(包括OSI,IP,两者对比和相关概念)

众所周知&#xff0c;就像我们计算机领域中的任何东西一样&#xff0c;计算机网络也是个分层的体系结构&#xff0c;现代提出的结构就两种——OSI和TCP/IP&#xff0c;我们先来剖析并对比一下这两种模型&#xff0c;然后总结一下分层思想中的一些共性。 TCP/IP与OSI结构对比图 …