一、学习视频
【搜索旋转排序数组【基础算法精讲 05】】 https://www.bilibili.com/video/BV1QK411d76w/?share_source=copy_web&vd_source=dc0e55cfae3b304619670a78444fd795
二、跟练代码记录
(1)排序,时复O(nlogn)(函数)或O(n)(遍历寻找)
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
# 排序 时复O(nlogn) 或 O(n)
# 最大值肯定是峰值
idx = list(range(len(nums)))
idx.sort(key = lambda x : nums[x],reverse = True)
return idx[0]
(2)二分查找,参考学习视频。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
# 二分查找
# 爬坡
left, right = 0,len(nums) - 2
#nums[-1] = nums[n] = -∞,任何情况最后一个都不为峰顶
while left <= right:
mid = (left + right) // 2
if nums[mid] < nums[mid + 1]:
#nums[i] != nums[i + 1]
left = mid + 1
else:
right = mid - 1
return left
2024.3.25续:
二分,参考视频。视频将两种情况都包含了。
class Solution:
def findMin(self, nums: List[int]) -> int:
# 二分查找
# # 原序列
# if nums[0] <= nums[-1]:
# return nums[0]
l,r = 0,len(nums)-2 #闭区间
# 两段递增序列
while l <= r:
mid = (l + r) // 2
# if nums[l] >= nums[mid]: #有错
if nums[-1] > nums[mid]:
r = mid - 1
else:
l = mid + 1
# 如果-1位为最小值,则最后l会指向-1
return nums[l]
二分,参考视频。
class Solution:
def search(self, nums: List[int], target: int) -> int:
# 二分
def is_blue(i:int) -> bool:
end = nums[-1]
if nums[i] > end:
return target > end and nums[i] >= target
else:
return target > end or nums[i] >= target
# 闭区间
l,r = 0, len(nums)-1
while l <= r:
mid = (l + r) // 2
if is_blue(mid):
r = mid - 1
else:
l = mid + 1
if l == len(nums) or nums[l] != target:
return -1
return l
注意:寻找target的题需要验证target是否被找到。
三、课后作业
来自灵神题解(. - 力扣(LeetCode))。太牛了!!!我还能说什么!!
class Solution:
def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
# 二分,对行二分
left, right = 0, len(mat) - 2
while left <= right:
i = (left + right) // 2 #即mid
mx = max(mat[i])
if mx > mat[i + 1][mat[i].index(mx)]:
right = i - 1 #始终记住是未确认值的闭区间
else:
left = i + 1
return [left,mat[left].index(max(mat[left]))]
二分,参考灵神题解(. - 力扣(LeetCode))。想到了与区间右端点进行比较,但是没有想到与区间右端点加一进行比较(与前面区间【0,n-2】数据与n-1位进行比较相同)。与区间内端点进行比较容易漏解。
class Solution:
def findMin(self, nums: List[int]) -> int:
# # 求最小值
# return min(nums)
# 二分
l,r = 0,len(nums)-2
while l <= r:
mid = (l+r) // 2
if nums[mid] == nums[r+1]:
# 去掉右侧重复元素
r -= 1
elif nums[mid] < nums[r+1]:
r = mid - 1
else:
l = mid + 1
return nums[l]
在评论也看到另一种写法(. - 力扣(LeetCode)),将左端重复数去除即可。其实道理都差不多,就是将重复数去除即可(保证能够对最小值存在区间进行判断)。一个是在比较时去除(灵神);一个是在比较前就去除(评论),变为常规题型。
对左边数进行去除。来自评论解法(链接如上)。
class Solution:
def findMin(self, nums: List[int]) -> int:
# 二分2
# 对左边数进行去除
l,r = 0,len(nums)-2
while l <= r and nums[l] == nums[-1]:
# 跟参照物进行比较
l += 1
while l <= r:
mid = (l+r) // 2
if nums[mid] <= nums[-1]:
r = mid - 1
else:
l = mid + 1
return nums[l]
对右边数进行去除。根据同样的道理我写出了对右边数进行去除的代码:
class Solution:
def findMin(self, nums: List[int]) -> int:
# 二分3
# 对右边数进行去除
l,r = 0,len(nums)-2
while l <= r and nums[l] == nums[r+1]:
# 跟参照物进行比较
r -= 1
while l <= r:
mid = (l+r) // 2
if nums[mid] <= nums[r+1]: #注意等号
r = mid - 1
else:
l = mid + 1
return nums[l]
总结:二分好难,还需要多练练。
完
感谢你看到这里!一起加油吧!