剑指 Offer 53 - II. 0~n-1中缺失的数字

1:问题描述

来源:LeetCode

难度:简单


问题详情:
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

2:问题分析

2.1 时间复杂度和空间复杂度

在真正开始介绍各种算法前,先以表格形式展示各自的时间复杂度和空间复杂度,其中 n n n表示数组nums的长度。

算法时间复杂度空间复杂度
暴力查询 O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)
二分查找O( l o g ( n ) log(n) log(n)) O ( 1 ) O(1) O(1)

2.2 暴力查询

暴力查询的思想是遍历数组的每一个元素,并判断当前元素值是否与当前下标值是否相等,如果不相等,就说明当前下标值缺失,返回即可。
举例来说:

nums=[0,1,3]

在遍历到nums[2]时发现其并不是等于2,所以可以断定2是缺失的数据、

特例说明,对于nums=[0,1,2]这样看着并不缺失数据的数组,因为题目中明确说明缺失了一位,那么只能是3是缺失的数值。

2.2.1 代码

def missingNumber(self, nums: List[int]) -> int:
    for i, item in enumerate(nums):
        if i != item:
            return i
    return i+1

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

2.3 二分查找

题目中说了nums是个有序数组,对于有序数组的搜索问题,那么自然就能想到使用二分查找的方法。

2.3.1 二分查找1

一开始本人想到的是利用左右指针指向的数据之和与“两位中位数”之和的大小来判断到底是哪个区间缺失了。
举例来说:

nums=[0,1,3,4]
  1. l e f t = 0 , r i g h t = 3 , m i d 1 = ( l e f t + r i g h t ) / / 2 = 1 , m i d 2 = ( l e f t + r i g h t − 1 ) / / 2 = 1 left = 0, right=3, mid1 = (left + right)//2=1, mid2 = (left+right-1)//2=1 left=0,right=3,mid1=(left+right)//2=1,mid2=(left+right1)//2=1
  2. n u m s [ 0 ] + n u m s [ 3 ] = 4 , n u m s [ 1 ] + n u m s [ 1 ] = 3 , 4 < 3 nums[0]+nums[3]=4,nums[1]+nums[1]=3,4<3 nums[0]+nums[3]=4,nums[1]+nums[1]=3,4<3,而且可以看出是中位数的右区间缺失,说明右边的数据缺失, n u m s [ l e f t ] + n u m s [ r i g h t ] < n u m s [ m i d 1 ] + n u m s [ m i d 2 ] nums[left]+nums[right]<nums[mid1]+nums[mid2] nums[left]+nums[right]<nums[mid1]+nums[mid2]

但是这种想法实际实现出来,需要考虑的特殊情况太多,暂时不做过多介绍,贴出代码:

def missingNumber(self, nums: List[int]) -> int:
    length = len(nums)
    # 特殊处理[1,2]这类缺失0的数据
    if nums[0] != 0:
        return 0
    if length == 1:
        return length
    left, right = 0, length - 1
    missing = 'left'  # 表示中位数的左边还是右边缺失
    while left < right:
        t1 = nums[left] + nums[right]
        length = right - left + 1
        a = left + length // 2
        b = left + (length - 1) // 2
        t2 = nums[a] + nums[b]
        if t1 == t2:
            if nums[a] - nums[b] == 2:
                return nums[a] - 1
            if missing == 'right':
                return nums[left] - 1
            else:
                return nums[right] + 1
        # 比如0 1 2 3 4 5 6 7 中位数是3 和 4,如果缺失 1的话,那就是0 2 3 4 5 6 7,t1 = 7, t2 = 8
        # 也就是缺失中位数左边的数值,t1 < t2
        elif t1 < t2:
            missing = 'left'
            right = b

        else:
            missing = 'right'
            left = a

2.3.1 二分查找2

但是有另一种基于二分查找思想的解法。
举例来说:

nums=[0,1,3,4]
  1. l e f t = 0 , r i g h t = 3 left =0,right=3 left=0,right=3
  2. m i d = ( l e f t + r i g h t ) / / 2 = 1 mid = (left + right)//2=1 mid=(left+right)//2=1
  3. 如果 n u m s [ m i d ] = m i d nums[mid] =mid nums[mid]=mid,那么说明 [ l e f t , m i d ] [left,mid] [left,mid]区间内的数据是没有缺失的(这里可以看出来是与暴力查询的思想是相同的), l e f t = m i d + 1 left=mid+1 left=mid+1
  4. 如果不相等,那么说明 [ l e f t , m i d ] [left,mid] [left,mid]区间内的数据是缺失的, r i g h t = m i d − 1 right=mid-1 right=mid1

代码如下:

def missingNumber(nums: List[int]) -> int:
    left, right = 0, len(nums) - 1
    # 这里取=是为了判断left=right时,当前元素的数值是否等于索引
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == mid:
            left = mid + 1
        else:
            right = mid - 1
    return left

时间复杂度为 O ( l o g ( n ) ) O(log(n)) O(log(n)),空间复杂度为 O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little student

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值