*内容来自leetcode
1.搜索旋转排序数组
题目要求
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
思路
开始想的是先排个序再二分查找,可能用的是内置的sort()排序,直接超时了。。。
最后直接一想,挨个找不就得了。结果还真行,效率还挺高,就是不满足时间复杂度的要求。
class Solution:
def search(self, nums: List[int], target: int) -> int:
for i in range(len(nums)):
if nums[i] == target:
return i
return -1
实际上,开始想的先排序再查找,无论采用哪种排序方式都不能满足时间复杂度的要求。故不再考虑排序。
再回到此题的初始条件来看,数组并非是被随机打乱的,而是绕某一个数进行了旋转,这个数的左右侧数字的大小顺序并没有发生变化。同时旋转后左侧元素一定大于右侧元素。
所以考虑使用二分查找,先找到左右侧元素的分界处,再对左右进行二分查找。虽然说实现起来比较麻烦,不过时间效率确实有所提高。
class Solution:
def search(self, nums: List[int], target: int) -> int:
def binarySearch(left,right,nums):
while left <= right:
middle = (left + right) // 2
if nums[middle] == target:
return middle
if nums[middle] > target:
right = middle - 1
if nums[middle] < target:
left = middle + 1
return -1
#先对整个数组进行二分查找找到分界处
left = 0
right = len(nums)-1
if len(nums) == 1 and nums[0] == target:
return 0
elif len(nums) == 1:
return -1
while left <= right:
middle = (left + right) // 2
if nums[middle] > nums[middle+1]:
break
if nums[middle] < nums[middle-1]:
middle = middle - 1
break
if nums[middle] >= nums[left]:
left = middle + 1
if nums[middle] <= nums[right]:
right = middle - 1
#再对两边进行二分查找寻找target
res1 = binarySearch(0,middle,nums)
if res1 != -1 :
return res1
res2 = binarySearch(middle+1,len(nums)-1,nums)
elif res2 != -1 :
return res2
else:
return -1
官方思路的不同之处在于,在得到middle之后,先判断target在哪个部分,再进行查找,而不是对左右两半都进行查找,也更为简洁。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
else:
r = mid - 1
return -1
2.搜索二维矩阵 II
题目要求
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
提示:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-109 <= matrix[i][j] <= 109
每行的所有元素从左到右升序排列
每列的所有元素从上到下升序排列
-109 <= target <= 109
思路
这道题的关键在于需要一个高效的算法,最不高效的就是挨个遍历查找。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == target:
return True
return False
由于矩阵具有升序的特点,故可以对上面的代码进行优化。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
for i in range(len(matrix)):
if matrix[i][0] > target:
break
for j in range(len(matrix[0])):
if matrix[i][j] == target:
return True
if matrix[i][j] > target:
break
return False
在对每一行进行查找时,也可以用二分查找实现。不过感觉好像差不多。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
def binarySearch(left,right,nums):
while left <= right:
middle = (left + right) // 2
if nums[middle] == target:
return nums[middle]
if nums[middle] > target:
right = middle - 1
if nums[middle] < target:
left = middle + 1
return -1
n = len(matrix[0])
for i in range(len(matrix)):
if matrix[i][0] > target:
break
res = binarySearch(0,n-1,matrix[i])
if res == target:
return res
return False
#调用二分查找函数的解法
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
for row in matrix:
idx = bisect.bisect_left(row, target)
if idx < len(row) and row[idx] == target:
return True
return False
官方提出了一种Z字形查找方法
我们可以从矩阵 matrix的右上角 (0,n−1)进行搜索。在每一步的搜索过程中,如果我们位于位置 (x,y),那么我们希望在以 matrix的左下角为左下角、以 (x,y)为右上角的矩阵中进行搜索,即行的范围为 [x,m−1],列的范围为 [0,y]:
如果 matrix[x,y]=target,说明搜索完成;
如果 matrix[x,y]>target,由于每一列的元素都是升序排列的,那么在当前的搜索矩阵中,所有位于第 y 列的元素都是严格大于 target的,因此我们可以将它们全部忽略,即将 y 减少 1;
如果 matrix[x,y]<target,由于每一行的元素都是升序排列的,那么在当前的搜索矩阵中,所有位于第 x 行的元素都是严格小于 target的,因此我们可以将它们全部忽略,即将 x 增加 1。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m = len(matrix)
n = len(matrix[0])
x,y = 0,m-1
while x < n and y >= 0:
if matrix[y][x] == target:
return True
if matrix[y][x] > target:
y -= 1
if matrix[y][x] < target:
x += 1
return False