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

目录

题目

解题思路1——顺序比较法

解题思路2——哈希表

解题思路3——二分查找

题解1——顺序比较法

题解2——哈希表

题解3——二分查找


题目

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/que-shi-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例 1:

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

示例 2:

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

限制:

1 <= 数组长度 <= 10000




解题思路1——顺序比较法

将[0,n-1]范围内的n个数字,按照数值递增的顺序塞进一个容量为n的数组中,那么数组中每一个元素的值和其索引是一对一的关系,即:

array[index] = index

因此,现在将[0,n-1]范围内的n个数字,按照数组递增的顺序,移除其中一个数后,塞进一个容量为n-1的数组中,那么必然会出现这样的情况:

数组被分为了两个子序列,这两个子序列其中一个保持:

array[index] = index

的关系,而另一个子序列存在:

array[index] = index + 1

的关系。

因此,只要进行一轮顺序比较,找到第一个array[index] = index + 1或者是第一个array[index] != index的元素,返回其索引值便可以。该位置的索引值就是缺失的数字。




解题思路2——哈希表

在[0,n-1]的范围内寻找某一个缺失的数字,那么完全可以使用哈希表来进行。

使用这样的哈希函数:

H(Key) = Key % n

每次计算获得的哈希值就是哈希表的索引,如果当前哈希表对应索引的值为0,那么便将其置为1。这样一轮遍历下来,哈希表中必然存在并且只会存在一个位置的值为0,那么这个位置的值就是缺少的那一个元素。




解题思路3——二分查找

需要注意的是,这是一个有序的数组中的查找,因此难免会想起二分查找。但是二分查找需要一个确定的目标值,但是在这个题目中,确定的目标值target是未知条件,那么便需要换一种思路,直接简单粗暴的使用二分查找貌似行不通。

那么,转换一下思路,还是使用二分查找,但是需要在普通的二分查找上做一些改变。

前面分析过,题目给出的数组有一个重要的特征:

数组会被分为两个序列,每一个序列内部是连续的,并且其中一个序列满足:

array[index] = index

另一个序列满足:

array[index] ≠ index

此时可以想到,二分法中array[mid]和target的三种大小关系,不就正好可以概括为:

  1. array[mid] = target
  2. array[mid] ≠ target

这样一来,子序列存在两种关系,array[mid]和target也存在两种关系,那么便可以考虑将它们对应起来,就像这样:

  1. array[index] = index 和 array[mid] = target
  2. array[index] ≠ index 和 array[mid] ≠ target

那么,此时的二分法便有了这样的雏形:

int left = 0;
int right = length - 1;
while(left <= right){
    int mid = (right - left)/2 + left;
    if(array[mid] == mid){
        //code
    }else{
        //code
    }
}

如果是相等的情况,那就说明[left,mid]区域内数据是完整的,该区间段不可能缺少数据,但是从全局来看,必定会缺少一个数据,那么这个缺少的数据只可能存在于[mid+1,right]区间内,那么在[mid+1,right]区间内再进行二分查找,直到退出循环。那么此时有:

int left = 0;
int right = length - 1;
while(left <= right){
    int mid = (right - left)/2 + left;
    if(array[mid] == mid){
        left = mid + 1;
    }else{
        //code
    }
}

如果是不相等的情况,那就说明[left,mid-1]区域内的数据是不完整的,那么该区间段存在缺少数据的情况,因此下面接着在该区间段进行二分查找以确定一个位置。那么此时有:

int left = 0;
int right = length - 1;
while(left <= right){
    int mid = (right - left)/2 + left;
    if(array[mid] == mid){
        left = mid + 1;
    }else{
        right = mid - 1;
    }
}

当循环结束时,可以得到left、right、mid三个数据,那么缺少的数据的值必然是这三个数据中的一个,下面接着分析。

可以初步确定,需要返回的变量是left。

再来一组缺失元素在首部(即缺失的是0)的情况:

再来一组缺失元素在尾部(即缺失的是array.length)的情况:

 那么,返回left是正确的,此时二分查找的代码为:

int left = 0;
int right = length - 1;
while(left <= right){
    int mid = (right - left)/2 + left;
    if(array[mid] == mid){
        left = mid + 1;
    }else{
        right = mid - 1;
    }
}

return left;



题解1——顺序比较法

class Solution {
    public int missingNumber(int[] nums) {
        int i = 0;
        while(i<nums.length && nums[i] == i){
            i++;
        }

        return i;
    }
}



题解2——哈希表

class Solution {
    public int missingNumber(int[] nums) {
        int[] HashTable = new int[nums.length+1];
        for(int i=0;i<nums.length;i++){
            int HashCode = nums[i] % (nums.length + 1);
            HashTable[HashCode] = 1;
        }
        for(int i=0;i<=nums.length;i++){
            if(HashTable[i] == 0){
                return i;
            }
        }
        return 0;
    }
}



题解3——二分查找

class Solution {
    public int missingNumber(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = (right - left)/2 + left;
            if(nums[mid] == mid){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }

        return left;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值