题目描述
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0到n-1之内。在范围0到n-1的n个数字中有且只有一个数字不在该数组中,请找出这个数字。(题目来源:《剑指offer》53题)
思路
解法1 时间复杂度O(n):
首先可以对0 ~ (n~1)这n个数求和S1,然后对数组中的所有数求和S2,二者的差值S1-S2即为不在数组中的这个数!
缺点:没用利用好数组是递增排序的规律,复杂度为O(n),这里不再赘述。
解法2 时间复杂度O(log n):
假设数字m不在数组中,由于数组是排序的,所以m之前的数字在数组中的值与其下标应相等,m之后的值其值比下标都要大1,因此,我们要找的不在数组中的元素m就是数组中的第一个数值与下标不等的下标!由于数组是递增排序的,因此利用二分法!
二分法代码
int getMissingNumber(vector<int>& nums) {
if (nums.size() <= 0)
return 0;
int l = 0;
int r = nums.size() - 1;
/***************经典二分代码***************/
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] != mid) //每次使用时只需注意if条件语句满足什么条件时收缩右边界即可
r = mid;
else
l = mid + 1;
} //无论找到找不到最后都收缩于l=r的情况
/***************经典二分代码***************/
if (nums[l] == l) //最后判断一下是否找到了想要的结果:如果没找到,说明收缩在数组的最右侧边界处,此时结果应为下标+1********注意边界情况
return l + 1;
else //如果找到了直接返回下标1即可
return l;
}
经典二分模板:
while (l < r) {
int mid = (l + r) / 2;
if (/*此处填写满足收缩右边情况的判断语句*/)
r = mid;
else
l = mid + 1;
}
说明:
跳出循环的条件永远为l=r,但此时分为两种情况:
情况一:没有找到想要的结果,根据具体问题另做处理(此时为边界情况或特殊输入情况)
情况二:找到了想要的结果,此时即为l(或arr[l]等)