序言
下面都是lt的关于二分查找的题目。 好好干饭,好好努力,好好加油哦!
题目一:最长公共前缀
找到几个字符串中最长公共前缀。
例子:
Input: ["flower","flow","flight"]
Output: "fl"
Input: ["dog","racecar","car"]
Output: ""
二分法思路:
首先公共前缀 肯定是最前面的几个字符才会叫做 前缀。
那么我们先把字符串组中的第一个字符串flower作为比较标准
先折半 变成 所以bgn=0,end= 6-1=5
折半mid = (0+5)//2=2,所以字符是flower[0:mid+1] = flo
检查是否都在所以的字符串里:
(1) flo 并不是都在,
end = mid -1 = 1 ; mid = (0+1)//2 = 0
所以这次字符串是 flower[0:mid+1] = f
(2) f 都在:
bgn = mid + 1 = 1 ; mid = (bgn + mid)//2 = 1
所以这次字符是 flower[0:mid+1] = fl
这个时候 bgn = end = 1 不应该再下一次了 所以终止条件是 while bgn < end
class Solution:
def check(self,strs,tmp):
for s in strs:
if s.find(tmp)!=0: return False
return True
def longestCommonPrefix(self, strs: List[str]) -> str:
left = 0
right = len(strs[0]) -1
pivot = strs[0]
while left <= right:
mid = (left + right)//2
tmp = pivot[0:mid+1]
if self.check(strs,tmp):
left = mid + 1
else: right = mid -1
return pivot[0:right + 1] if right>=0 else ''
题目二:数组插入值
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置
输入: [1,3,5,6], 5
输出: 2
输入: [1,3,5,6], 2
输出: 1
输入: [1,3,5,6], 7
输出: 4
思路是:
这里把题目转换一下,就是如果target比 数组的数都大的话,那么直接输出长度
否则的话,输出刚好比target大或者等于的数的index。
用二分法寻找
当bgn = end的时候 ,仍在在循环内,
如果target 大于 tmp的时候,即表示当前tmp小于target,需要往前一步再返回。
如果target 小于 tmp的时候,即表示当前的tmp都大于target,那直接返回bgn即可
二分法:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
#special calse
if target > nums[-1]: return len(nums)
left = 0
right = len(nums)-1
while left < right:
mid = (left+right)//2
if nums[mid] == target: return mid
elif nums[mid] > target:
right = mid
else:
left = mid + 1
return left
题目三:求开方跟
实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去
输入: 4
输出: 2
----------------
输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
用二分法来做
把(0,x)中,即bgn = 0 ,end = x
mid = (bgn + end )//2 = x//2 tmp = mid * mid
比较 tmp和x 如果tmp大的话,则需要让mid变小,则 end要变小 即end变成 mid-1
比较 tmp和x 如果tmp小的话,则需要让mid 表大,则bgn 要变大,即bgn 变成 mid+1
if tmp = x则返回mid
最后结束之后,返回 end 为什么不返回 bgn 这是因为这次我们要返回的是更小的数,
如果是返回大的数,则返回bgn 返回小的数,返回end。
def mySqrt(x):
bgn = 0
end = x
while bgn <= end:
mid = (bgn + end)//2
tmp = mid*mid
if tmp > x:
end = mid - 1
elif tmp < x:
bgn = mid + 1
else:
return mid
return end
这道题也可以用牛顿法。
牛顿法的思想是
初始化某一个点,然后求这个点的切线之后,再求这个切线在横坐标的x 作为更新的值
终止条件是 连续的两个x值 相差极小 0.000001
这个方法肯定会超过规定的时间复杂度
def mySqrt(x):
if x == 0: return x
#初始值 x0 = x
x0 =x
while True:
x1 = 1/2 * (x/x0 + x0)
if abs(x1 - x0) < 0.000001:
break
x0 =x1
return int(x0)
题目四:twoSum问题
给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
之前用了哈希表来求,现在用二分法来做
思路是
现在有两个数,怎么用二分法呢? 思路是固定一个值,然后在剩下的数组中,寻找另一个值。用二分法来找
def twoSum(numbers, target):
for i in range(len(numbers)):
bgn = i
end = len(numbers) -1
while bgn <= end:
mid = (bgn + end )//2
tmp = numbers[mid]
if tmp < target - numbers[i]:
bgn = mid + 1
elif tmp > target - numbers[i]:
end = mid - 1
elif tmp == target - numbers[i]:
return [i,mid]
return []
题目五:LT287-寻找重复值
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间
输入:nums = [1,3,4,2,2]
输出:2
如果直接使用哈希表的话,时间复杂度和空间复杂度都是O(N),不能满足要求
如果用set函数的话,那也是占据O(N)的空间,也是不满足要求
所以这里有一个抽屉法的二分法。
举一个例子就是:
cnt表示的是 比当前值小或者等于的个数有多少个。
比如 在这个例子中,bgn=1, end = 4 所以mid = (bgn + end)//2 = 2
所以在nums中,小于或者等于mid的有3个。3 > mid = 2 所以答案是 在 bgn ~ mid 之间。
如果大于mid的话,则在mid+1 ~ end之间。 最后返回bgn
class Solution:
def cnt(self,nums,mid):
cnt = 0
for num in nums:
if num <= mid: cnt += 1
return cnt
def findDuplicate(self, nums: List[int]) -> int:
#考虑抽屉的二分法
#由于是1,2,3,4,n中有一个数是重复的是 所以
bgn = 1
end = len(nums) - 1
while bgn < end:
mid = (bgn + end)//2
cnt = self.cnt(nums,mid)
if cnt > mid: end = mid
else : bgn = mid + 1
return bgn
这里的时间复杂度是 O(nlogn)
空间复杂度是O(1)