c语言二分法查找一个数_一日一力扣第45天:寻找重复数

点击上方蓝字关注加小星星✨

涛哥给你我所有~

今天是 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 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

按理这道题其实并不难,但是加了如下的条件就变态了

  1. 不能更改原数组(假设数组是只读的);
  2. 只能使用额外的 O(1) 的空间。

如果没有额外限制条件,我们很容易想到的方法有:

  1. 使用哈希表判重,这违反了限制 2;
  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 元红包一个!

a0c2f038f888f993aa3730b3e4039914.png
长按扫码,一起进步
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值