二分查找
704. 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
核心思想:标准二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums) == 0: return -1
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target: return mid
elif nums[mid] > target: right = mid - 1
else: left = mid + 1
return -1
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
核心思想:在二分查找的基础上需要进一步判断插入的位置,即在二分查找没找到的情况下,left和right指针的位置
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target: return mid
elif nums[mid] > target: right = mid - 1
else: left = mid + 1
return right + 1
注意:如果target没有数组中,那么left和right最终会指向同一个位置,且这个位置是小于target的位置,所以最后返回的结果是right+1
笔记:二分查找返回的mid对应的元素一定是小于等于target的!!!
解法二:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)-1
res = len(nums)
while left <= right:
mid = (left + right) // 2
if nums[mid] >= target:
res = mid
right = mid - 1
else:
left = mid + 1
return res
本地属于大于等于x的最小下标问题
注意:当寻求的mid的对应元素小于等于target时,res放在大于等于判断的下面
34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
核心思想:在二分查找的基础上需要进一步判断target在数组中的范围,没找到返回[-1, -1],找到了就在寻找target的范围
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
left, right = 0, len(nums)-1
res = -1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
res = mid
break
elif nums[mid] > target: right = mid - 1
else: left = mid + 1
if res == -1: return [-1, -1]
else:
left = right = mid
while left >= 0 and nums[left] == target: left -= 1
while right < len(nums) and nums[right] == target: right += 1
return [left+1, right-1]
注意:最后两个while的判断,一定要把范围的判断放在前面,不然会报错
69. x 的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
核心思想:在二分查找的基础上,在找不到的情况下,需要判断mid的位置
class Solution:
def mySqrt(self, x: int) -> int:
left, right = 0, x
while left <= right:
mid = (left + right) // 2
val = mid * mid
if val == x: return mid
elif val > x: right = mid - 1
else: left = mid + 1
if mid * mid > x: return mid - 1
else: return mid
注意:最后返回的时候,需要判断mid的大小,如果mid的平方大于了x,则mid需要-1,否则直接返回mid
解法二:
class Solution:
def mySqrt(self, x: int) -> int:
left, right = 0, x
res = 0
while left <= right:
mid = (left + right) // 2
val = mid * mid
if val <= x:
res = mid
left = mid + 1
else:
right = mid - 1
return res
本题属于小于等于x的最大下标问题
注意:当寻求的mid的对应元素(mid*mid)小于等于target(n)时,res放在小于等于判断的下面
367. 有效的完全平方数
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要 使用任何内置的库函数,如 sqrt 。
核心思想:类似于求平方根,唯一不同的是找不到返回False
class Solution:
def isPerfectSquare(self, num: int) -> bool:
left, right = 0, num
while left <= right:
mid = (left + right) // 2
square = mid * mid
if square == num: return True
elif square > num: right = mid - 1
else: left = mid + 1
return False
441. 排列硬币
你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。
给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。
核心思想:利用二分查找找到那个mid,使得其等差和为n
class Solution:
def arrangeCoins(self, n: int) -> int:
left, right = 0, n
while left <= right:
mid = (left +right) // 2
sums = mid * (mid + 1) /2
if sums == n: return mid
elif sums > n: right = mid - 1
else: left = mid + 1
return right
解法二:
class Solution:
def arrangeCoins(self, n: int) -> int:
left, right = 0, n
res = 0
while left <= right:
mid = (left + right) // 2
if mid * (mid + 1) / 2 <= n:
res = mid
left = mid + 1
else:
right = mid - 1
return res
同x的平方根一样,本题属于小于等于x的最大下标问题