目录
题目
一个长度为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的三种大小关系,不就正好可以概括为:
- array[mid] = target
- array[mid] ≠ target
这样一来,子序列存在两种关系,array[mid]和target也存在两种关系,那么便可以考虑将它们对应起来,就像这样:
- array[index] = index 和 array[mid] = target
- 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;
}
}