题目
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
思路
-
暴力法:遍历
最简单的方法就是遍历一遍数组,找到不存在的数字。思路一是想着依次比较数组中相邻的两个数,如果nums[i]与num[i-1]的差大于1,那么缺少的数就一定是num[i]-1。同时还需要考虑第一个数是不是0以及是否缺少n的特殊情况。此时,代码的时间效率极低,88ms。
class Solution:
def missingNumber(self, nums: List[int]) -> int:
length = len(nums)
if length == 1:
return length - nums[0]
else:
if nums[0] != 0:
return 0
else:
flag = 0
for i in range(1, length):
if nums[i] - nums[i-1] != 1:
flag = 1
return nums[i] - 1
if flag==0:
return length
后来考虑到既然是在一连串递增数字中找到缺少的一个数,这里是关键,只缺1个数。因此我们可以用n个数字之和减去nums中所有数字之和。
import numpy as np
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
return length*(1+length)/2 - np.sum(nums)
提交后,用时60ms。相比于遍历提升并不是很大,于是尝试着用python自带的求和函数提交。
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
return length*(1+length)/2 - sum(nums)
此时,用时28ms。在改代码的过程中,还有一点发现就是,不要在程序中import无用的包。我将np.sum()换为sum()时,忘记去掉import numpy as np,提交后程序用时为40ms。
- 二分
有序数组用二分。
前面暴力处理并没有直接利用有序这一特点。在处理这道题目时,采用二分法的方式,我们定义两个变量i,j分别指向数组的首元素和尾元素,同时定义mid = (i+j)/2,如果nums[mid] = mid的话,就表明从i到mid之间并不缺少数,所以缺少的数肯定在mid+1和j之间,改变i的值为mid+1;而若nums[mid] != mid, 就表明i到mid之间肯定缺少一个数,改变j的值为mid-1。详细代码如下:
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
i = 0
j = length - 1
while i<=j:
mid = (i+j)/2
if nums[mid] == mid:
i = mid + 1
else:
j = mid - 1
return i
总结
- 提到有序,想二分
- 能用python自带函数,就不要用numpy中定义的函数
- 不要import无用的包