class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1 # 确定闭区间
while left <= right:
mid = left + int((right - left)/2)
if nums[mid] == target:
return mid
if nums[mid] < target:
left = mid + 1
if nums[mid] > target:
right = mid - 1
return -1
思路是找到target的左边界,然后向后寻找,直到元素值不等于target
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums) > 0: #确保给的数组长度不为0
left = 0
right = len(nums) - 1 #确定闭区间
index = 0
while left <= right:
mid = left + int((right - left)/2)
if nums[mid] == target: #确定左边界,就要把right向左收缩
right = mid - 1
if nums[mid] < target:
left = mid + 1
if nums[mid] > target:
right = mid - 1
if left < len(nums) and nums[left] == target: #跳出循环时,left=right+1,要去检查left此时是否超出了下标,并且是否为target。
index = left
while index < len(nums) and nums[index] == target:
index += 1 #让index一直向后,直到不再等于target
return index - left if (index - left) > 0 else 0 #确保输出的数大于0
else:
return 0
二分查找内容总结:
(1)确定闭区间:
left = 0
right = len(nums) - 1
(2)确定循环终止条件:
while left <= right
(3)如果要寻找target:
if num[mid] == target:
return
如果要寻找左边界,则将right不断向左收缩,跳出循环后,left = right + 1,要检查left
if num[mid] == target:
right = mid -1
if left < len(num) and num[left ] == target:
return
如果要寻找右边界,则将left不断向右收缩,跳出循环后,right = left -1 ,要检查right
if num[mid] == target:
left = mid +1
if right > 0 and num[right ] == target:
return
(4)while循环中其他情况:小了就改变左边界,大了就改变右边界
if nums[mid] < target:
left = mid + 1
if nums[mid] > target:
right = mid - 1
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) -1
while left <= right:
mid = left + int((right - left)/2)
if nums[mid] == target:
right = mid -1 #找左边界
if nums[mid] < target:
left = mid + 1
if nums[mid] > target:
right = mid - 1
if left < len(nums) and nums[left] == target or nums[left-1] < target:#找到了该值 或 找到比它大一点的值
return left
if left >= len(nums) : #全部元素都小于该值
return len(nums)
else: #全部元素都大于该值
return 0
当有序数组中有两两相等,有且只有一个数没有出现两次,可以用两数和来找到该数。
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
left = 0
right = len(nums) - 1
if len(nums) >= 2: #数组中得大于两个数
while left < right:
if nums[left] + nums[right] == nums[left+1] + nums[right-1]:
left += 2
right -= 2
else:
if nums[left] != nums[left+1]:
return nums[left]
if nums[right] != nums[right-1]:
return nums[right]
if left == right:
return nums[right] #跳出循环后,如果仍没有找到该数,那该数应该是left和right共同指向
else:
return nums[0]
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
left = 0
right = len(nums) - 1
index = 0
if len(nums) > 0:
while left <= right:
mid = left + int((right - left)/2)
if nums[mid] == target:
right = mid -1 #找左边界
if nums[mid] < target:
left = mid + 1
if nums[mid] > target:
right = mid - 1
if left < len(nums) and nums[left] == target: #找到了该值的左边界
index = left
while index < len(nums) and nums[index] == target:
index += 1
return [left,index-1]
else: #没有该值
return [-1,-1]
else:
return [-1,-1]
class Solution:
def mySqrt(self, x: int) -> int:
if x == 1 or x == 0:
return x
else:
right = int(x/2) #x的平方根应该不会大于x的一半
left = 1
while left <= right:
mid = left + int((right - left)/2)
if mid*mid == x:
return mid
if mid*mid > x:
right = mid - 1
if mid*mid < x : #在这个范围内有可能找到值
if (mid+1)*(mid+1) > x: #如果正好卡在两个数中间
return mid
else:
left = mid + 1 #移动left
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
outlist = float('-inf') #负无穷
nums.insert(0,outlist)
nums.append(outlist) #以防数组超出下标,所以给数组前后都加上负无穷
letf = 1
right = len(nums) - 2
while letf <= right:
mid = letf + int((right - letf)/2)
if nums[mid] > nums[mid-1] and nums[mid] > nums[mid + 1]:
return mid - 1 #当随机选择的值满足条件时
if nums[mid] < nums[mid + 1]:
letf = mid + 1 #当i 小于i+1时,就往右走,修改左边界,让值往大走
if nums[mid] > nums[mid + 1]:
right = mid - 1 #当i大于i+1时,就往左走,修改右边界,让值往大走
思路解析:
在n+1个元素的数组中,每个元素的值[1,n],那至少有一个重复出现的数字,当只有一个数字重复,那它可能重复多次。
首先,我们要寻找某个数的时候,先想一下二分查找。我们需要两个指针:left和right,其次target就是我们要找到的重复元素。最后我们需要找到target的比较条件。
假设count表示对于值i来说,数组nums中有多少个不大于i的数。
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
left = 1
right = len(nums)
while left <= right:
mid = int((right+left)/2)
count = 0
for i in nums:
count += i <= mid
if count > mid:
right = mid - 1
if count <= mid:
left = mid + 1
return left
class Solution:
def nextGreatestLetter(self, letters: List[str], target: str) -> str:
left = 0
right = len(letters) - 1
while left <= right:
mid = left + int((right-left)/2)
if letters[mid] <= target: #如果此时的字母小于等于目标,则向右寻找
left = mid + 1
if letters[mid] > target:
right = mid - 1
if left < len(letters) : #跳出循环之后判断一下left,如果left超出下标,证明数组中全部数都不符合
return letters[left]
else:
return letters[0]
class Solution:
def reachNumber(self, target: int) -> int:
s = 0
n = 0
target = abs(target) #正负都是一样的结果
while s < target or (s - target) % 2:#未到终点或者距离终点是奇数
n += 1
s += n
return n
也就是一共是两种结果:
(1)走了n步之后,距离目标是偶数,则和向前一直走到达目标的n是一致的。
(2)走了s步之后,距离目标是奇数。
在经过的步长s>=target而且距离目标只有偶数的时候,此时经过的n步才是最终结果。即 while循环的条件是 s<target or (s-target)%2 ,在没有超过目标或者距离目标是奇数,都要继续走。