LeetCode 第 287 号问题:寻找重复数,一道非常简单的数组遍历题,加上四个条件后感觉无从下手
今天分享的题目来源于 LeetCode 第 287 号问题:寻找重复数。
题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
题目解析
个人博客:www.cxyxiaowu.com
给定一个整形数组,数组的长度是 n + 1,数组里面存放的元素是区间 [1, n] 上的数,这个数组中有且仅有一个元素是重复的,题目让我们找出这个元素,并且这道题给了如下的限制:
- 不能改变数组
- 只能使用 O(1) 的空间
- 时间复杂度必须小于 O(n^2)
- 重复的元素可重复多次
首先不能改变数组导致无法排序,也无法用 index 和元素建立关系;
只能使用 O(1) 的空间意味着使用哈希表去计数这条路也走不通;
时间复杂度必须小于 O(n^2) 表示暴力求解也不行;
重复的元素可重复多次 这一条加上后,本来可以通过累加求和然后做差 sum(array) - sum(1,2,...,n) 的方式也变得不可行。
本来是非常简单的一道数组遍历的题目,加上了上面这四个条件后感觉有点无从下手。
我们说做题要借助算法,而不是空想,因此对于这道题也不例外,我们可以问自己这样一个问题,就是 “什么样的算法可以不使用额外的空间解决数组上面的搜索问题?”。
静静的思索一下。
你这时应该隐隐约约知道了,难道是 二分查找!
什么?二分查找法?!
二分查找法不是对有序数组才适用么?
这里澄清一个误区,二分法的使用 并不一定 需要在排序好的数组上面进行,不要让常见的例题限制了你的思路,二分法还有一个比较高级的用法叫做 按值二分。
这道题目交代的信息很少,我们只需要关注两个东西 - 数组,数组里的元素,利用二分我们需要去思考的是,我们要找符合条件的元素作为答案,那么比答案小的元素具有什么样的特质,比答案大的元素又具有什么样的特质?,结合题目给我们的例子来看看:
( 说明:下面的 <= 符号表明 小于或者等于。)
例1:
[1,3,4,2,2] 元素个数
<= 1 的元素:1 1
<= 2 的元素:1, 2, 2 3
<= 3 的元素:1, 2, 2, 3 4
<= 4 的元素:1, 2, 2, 3, 4 5
例2:
[3,1,3,4,2]
<= 1 的元素:1 1
<= 2 的元素:1, 2 2
<= 3 的元素:1, 2, 3, 3 4
<= 4 的元素:1, 2, 3, 3, 4 5
极端一点的例子 (必须保证数组的长度是 n + 1, 并且元素都在区间[1,n] 上, 有且只有一个重复)
[3,3,3,3,4]
<= 1 的元素: 0
<= 2 的元素: 0
<= 3 的元素:3, 3, 3, 3 4
<= 4 的元素:3, 3, 3, 3, 4 5
看完上面几个例子,相信你明白了一个事实:
“如果选中的数 小于 我们要找的答案,那么整个数组中小于或等于该数的元素个数必然小于或等于该元素的值;
如果选中的数 大于或等于 我们要找的答案,那么整个数组中小于或等于该数的元素个数必然 大于 该元素的值”
而且你可以看到,我们要找的答案其实就处于一个分界点的位置,寻找边界值,这又是二分的一个应用,而且题目已经告诉我们数组里面的值只可能在 [1, n] 之间,这么一来,思路就是在 [1, n] 区间上做二分,然后按我们之前提到的逻辑去做分割。整个解法的时间复杂度是 O(nlogn),也是满足题目要求的。
上面的解法不是最优的,但是个人觉得是根据现有的知识比较容易想到的。
另外一种 O(n) 的解法借鉴快慢指针找交点的思想,算法非常的巧妙,也非常的有趣,但不太容易想到,这里把代码放上。
代码实现一
//二分查找
class Solution {
public int findDuplicate(int[] nums) {
int len = nums.length;
int start = 1;
int end = len - 1;
while (start < end) {
int mid = start + (end - start) / 2;
int counter = 0;
for (int num:nums) {
if (num <= mid) {
counter++;
}
}
if (counter > mid) {
end = mid;
} else {
start = mid + 1;
}
}
return start;
}
}
代码实现二
//快慢指针
public int findDuplicate(int[] nums) {
int fast = nums[nums[0]];
int slow = nums[0];
while (fast != slow) {
fast = nums[nums[fast]];
slow = nums[slow];
}
slow = 0;
while (fast != slow) {
fast = nums[fast];
slow = nums[slow];
}
return slow;
}
❤️ 看完三件事:
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个忙:
- 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
- 关注我和专栏,让我们成为长期关系
- 关注公众号「五分钟学算法」,第一时间阅读最新的算法文章,公众号后台回复 1024 送你 50 本 算法编程书籍。
LeetCode 第 287 号问题:寻找重复数,一道非常简单的数组遍历题,加上四个条件后感觉无从下手的更多相关文章
- [LeetCode] Find the Duplicate Number 寻找重复数
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), pro ...
- LeetCode:寻找重复数【287】
LeetCode:寻找重复数[287] 题目描述 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数.假设只有一个重复的整数 ...
- Leetcode之二分法专题-287. 寻找重复数(Find the Duplicate Number)
Leetcode之二分法专题-287. 寻找重复数(Find the Duplicate Number) 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 ...
- [LeetCode] 287. Find the Duplicate Number 寻找重复数
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), pro ...
- 【Leetcode】287. 寻找重复数(数组模拟链表的快慢指针法)
寻找重复数 根据题意,数组中的数字都在1~n之间,所以数字的范围是小于数组的范围的,数组的元素可以和数组的索引相联系. 例如:nums[0] = 1 即可以将nums[0]作为索引 通过nums[0] ...
- Java实现 LeetCode 287 寻找重复数
287. 寻找重复数 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数.假设只有一个重复的整数,找出这个重复的数. 示例 ...
- 287. 寻找重复数 Java解法
287. 寻找重复数 这题的难点就在于下面的说明了,我们先不管下面的那些说明的要求,用常规的解法来解答下上的题目. 排序思想解法 先把原来的数组进行排序,然后逐个遍历,一旦发现后一个元素和当前的元素相 ...
- Leetcode之二分法专题-744. 寻找比目标字母大的最小字母(Find Smallest Letter Greater Than Target)
Leetcode之二分法专题-744. 寻找比目标字母大的最小字母(Find Smallest Letter Greater Than Target) 给定一个只包含小写字母的有序数组letters ...
- Leetcode之二分法专题-154. 寻找旋转排序数组中的最小值 II(Find Minimum in Rotated Sorted Array II)
Leetcode之二分法专题-154. 寻找旋转排序数组中的最小值 II(Find Minimum in Rotated Sorted Array II) 假设按照升序排序的数组在预先未知的某个点上进 ...
随机推荐
- 阿里云 windows frp 远程桌面
环境: 阿里云服务器 server 2008 ,想要被远程访问的终端(本机)是win7 x64 目的:实现在别的地方(家里,出差在外) 用 远程桌面 访问 位于公司内部的电脑 frp 介绍:https ...
- 008 Python基本语法元素小结
目录 一.概要 二.保留字 三.温度转换 一.概要 缩进.注释.命名.变量.保留字 数据类型.字符串. 整数.浮点数.列表 赋值语句.分支语句.函数 input().print().eval(). p ...
- Git使用(二)版本库创建及文件修改
一.创建版本库 1.安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功! 安装完成后,还需要最后一步设置,在命令行输入: $ gi ...
- ES6-数组的新方法
1.Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型. Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7)创建一个 ...
- 解析 HTTP 请求 header 错误
1.org.apache.coyote.http11.Http11Processor.service 解析 HTTP 请求 header 错误 2.原因:在创建项目名称的时候,文件名不能带有中文,只能 ...
- CSS——段落处理
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- centos php7 安装mysqli扩展心得
在新配服务器时发现,php无法连接到mysql.通过phpinfo发现.根本没有显示mysqli的相关配置.经过一系列研究.总结了下.: 第一步: 在phpinfo里没有mysqli配置,原因是安装p ...
- C++程序设计学习
第一章 预备知识 1.C++历史起源 由于C语言具有许多优点,比如语言简洁灵活:运算符和数据类型丰富:具有结构化控制语句:程序执行效率高:同时具有高级语言和汇编语言的优点等.与其他高级语言相比,C语言 ...
- git使用和操作
git提交日志的规范 为了更规范的开发,特别是团队协同开发,对于代码托管工具的提交上都会有要求的. 作为开发者,我们一定要注重提交日志的规范性,我们要对自己写的代码负责.提交日志规范很多,最近看到了一 ...
- 【深入学习MySQL】MySQL的索引为什么使用B+树?
前言 在MySQL中,无论是Innodb还是MyIsam,都使用了B+树作索引结构(这里不考虑hash等其他索引).本文将从最普通的二叉查找树开始,逐步说明各种树解决的问题以及面临的新问题,从而说明M ...