点击上方蓝字关注加小星星✨
涛哥给你我所有~
今天是 Kevin 的算法之路的第 45 天。为大家讲解 LeetCode 第 287 题,是一道(据说)花费了计算机科学界的传奇人物Don Knuth
24小时才解出来的题目。
每日一笑
寒武纪期间,节肢动物大量出现,假肢供不应求
题目描述
给定一个包含 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) 。数组中只有一个重复的数字,但它可能不止重复出现一次。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-the-duplicate-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
按理这道题其实并不难,但是加了如下的条件就变态了
- 不能更改原数组(假设数组是只读的);
- 只能使用额外的
O(1)
的空间。
如果没有额外限制条件,我们很容易想到的方法有:
- 使用哈希表判重,这违反了限制 2;
- 将原始数组排序,排序以后,重复的数相邻,即找到了重复数,这违反了限制 1;
所以,我首先使用的是很简单粗暴的「双重遍历判断」,虽然符合题意但是效率很慢。
//go
func findDuplicate(nums []int) int {
length := len(nums)
for i := 0; i for j := i+1; j if nums[i] == nums[j] {
return nums[i]
}
}
}
return 0
}
后来,在题解里看到大佬的「二分查找」方法很赞,也比较易理解,分享一下。
预备知识:
抽屉原理:桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放不少于两个苹果。
这题的数字就如苹果,肯定有一个是多出来的,所以我们可以先猜一个数(取有效范围 [left, right]
的中位数 mid
),然后统计原始数组中小于等于mid
的个数cnt
,如果cnt
大于mid,则重复元素就在区间[left, mid]
中
//go
func findDuplicate(nums []int) int {
length := len(nums)
left, right := 1, length-1
for left mid := (left + right)>>1 //(left + right)/2 的位运算写法
cnt := 0
for _, num := range nums {
if num <= mid {
cnt++
}
}
if cnt > mid {
right = mid
}else {
left = mid+1
}
}
return left
}
- 时间复杂度:O(NlogN),二分法的时间复杂度为O(logN),在二分法的内部,执行了一次 for 循环,时间复杂度为O(N),故时间复杂度为O(NlogN)。
- 空间复杂度:O(1),使用了一个 cnt 变量,因此空间复杂度为 O(1)。
郑重声明:
所展示代码已通过 LeetCode 运行通过,请放心食用~
在唠唠嗑
很多人都想养成好习惯,但大多数人却是三分钟热度。为了我们能一起坚持下去,决定制定如下计划(福利)
一起学算法,打卡领红包!
参与方式:
关注我的微信公众号「Kevin的学堂」
还可「加群」与众多小伙伴
一起坚持,一起学习,一起更优秀!
打卡规则为:
「留言」“打卡XXX天” ➕「分享」到朋友圈
奖励:
连续打卡
21
天,联系本人获取6.6
元红包一个!连续打卡
52
天,联系本人获取16.6
元红包一个!连续打卡
100
天,联系本人获取66.6
元红包一个!