1 二分查找 1. 思路 题目: 给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。704. 二分查找 - 力扣(LeetCode)
二分存在两种搜索的方法,(1)左闭右闭,(2)左闭右开。尽量使用一种写法,推荐使用第一种。
左闭右闭: right
的长度为 len(nums) - 1
,while 循环中的判断为 left<=right
,并且当更新right
的长度时,使用mid - 1
。
左闭右开: right
的长度为 len(nums)
,while 循环中的判断为 left<right
,并且当更新right
的长度时,使用mid
。
2. 解法 Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def search (self, nums: List [int ], target: int ) -> int : l = 0 r = len (nums) - 1 while l <= r: mid = (l+r)//2 if nums[mid] == target: return mid if nums[mid] > target: r = mid - 1 if nums[mid] <target: l = mid + 1 return -1
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int search (vector<int >& nums, int target) { int l = 0 , r = nums.size () - 1 ; while (l<=r){ int mid = l + (r - l) / 2 ; if (nums[mid] > target){ r = mid - 1 ; } if (nums[mid] < target){ l = mid + 1 ; } if (nums[mid] == target){ return mid; } } return -1 ; } };
3. 变种 (1) 题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。35. 搜索插入位置 - 力扣(LeetCode)
由于数组中可能存在没有target
的情况,而这种情况需要返回target
可以插入的索引。
如果while(l<=r)
则我们需要改变 l
的索引, 如果使用第二种二分while(l<r)
,则不需要做出改变。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def searchInsert (self, nums: List [int ], target: int ) -> int : l = 0 r = len (nums) - 1 while l <= r: mid = l + (r - l) // 2 if nums[mid] == target: return mid if nums[mid] > target: r = mid - 1 if nums[mid] < target: l = mid + 1 return l
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : int searchInsert (vector<int >& nums, int target) { int l = 0 , r = nums.size (); while (l < r){ int mid = l + (r - l) / 2 ; if (nums[mid] == target) return mid; if (nums[mid] > target) r = mid; if (nums[mid] < target) l = mid + 1 ; } return l; } };
(2) 题目: 给你一个按照非递减顺序排列的整数数组 nums
,和一个目标值 target
。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target
,返回 [-1, -1]
。你必须设计并实现时间复杂度为 O(log n)
的算法解决此问题。34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
思路:寻找target
的起始位置和结束位置。其实就是从左边开始二分查找,找到第一个target,letfidx = mid, r = mid - 1
;然后从右边开始二分,找到最后一个target,rightidx = mid, l = mid + 1
。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution : def searchRange (self, nums: List [int ], target: int ) -> List [int ]: l, r = 0 , len (nums) - 1 left = right = -1 while (l <= r): mid = (l + r) // 2 if nums[mid] == target: left = mid r = mid - 1 if nums[mid] < target: l = mid + 1 if nums[mid] > target: r = mid - 1 l = left if left != -1 else 0 r = len (nums) - 1 while (l <= r): mid = (l + r) // 2 if nums[mid] == target: right = mid l = mid + 1 if nums[mid] < target: l = mid + 1 if nums[mid] > target: r = mid - 1 return [left, right]
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : vector<int > searchRange (vector<int >& nums, int target) { int l = 0 , r = nums.size () - 1 ; int left = -1 , right = -1 ; while (l<=r){ int mid = (l + r) / 2 ; if (nums[mid] == target){ left = mid; r = mid - 1 ; } if (nums[mid] < target) l = mid + 1 ; if (nums[mid] > target) r = mid - 1 ; } l = (left != -1 ) ? left : 0 ; r = nums.size () - 1 ; while (l <= r){ int mid = (l + r) / 2 ; if (nums[mid] == target){ right = mid; l = mid + 1 ; } if (nums[mid] < target) l = mid + 1 ; if (nums[mid] > target) r = mid - 1 ; } vector<int > ans{left, right}; return ans; } };
(3) 题目: 给你一个非负整数 x
,计算并返回 x
的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意: 不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。69. x 的平方根 - 力扣(LeetCode)
思路:由于不能使用math函数库,因此可以使用二分的方法,通过mid**2
来判断,从而得到ans
的值。因为 \(k^2 <= x\) ,所以通过二分查找来找到 k
,然后通过移动左右边界来定位 mid
。
Python
1 2 3 4 5 6 7 8 9 10 11 class Solution : def mySqrt (self, x: int ) -> int : l, r, ans = 0 , x, -1 while l <= r: mid = l + (r - l) // 2 if mid ** 2 <= x: ans = mid l = mid + 1 else : r = mid - 1 return ans
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int mySqrt (int x) { int l = 0 , r = x, ans = -1 ; while (l <= r){ int mid = l + (r - l) / 2 ; if ((long long )mid * mid <= x){ ans = mid; l = mid + 1 ; }else { r = mid - 1 ; } } return ans; } };
(4) 题目: 给你一个满足下述两条属性的 m x n
整数矩阵:
每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target
,如果 target
在矩阵中,返回 true
;否则,返回 false
。74. 搜索二维矩阵 - 力扣(LeetCode)
思路:对于二维有序的矩阵,进行元素搜索时也可以使用二分。可以使用二次二分进行查找,也可以使用一次二分进行查找。
两次二分查找:就是先对行或列
进行查找,确定元素在哪列或行
,然后对确定的那列或行
进行第二次查找,最终找到元素。 一次二分查找:就是将二维矩阵看为一维数组,通过取整取余
操作来判断元素位置,mid / n
为元素的行,mid % n
为元素列(因为每n个元素后才会换行) Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution : def searchMatrix (self, matrix: List [List [int ]], target: int ) -> bool : n = len (matrix[0 ]) - 1 m = len (matrix) - 1 top = 0 left = 0 while top <= m: mid = top + (m - top) // 2 if matrix[mid][left] < target: top = mid + 1 elif matrix[mid][left] > target: m = mid - 1 else : return True top -= 1 while left <= n: mid = left + (n -left) // 2 if matrix[top][mid] < target: left = mid + 1 elif matrix[top][mid] > target: n = mid - 1 else : return True return False
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : bool searchMatrix (vector<vector<int >>& matrix, int target) { int m = matrix.size (); int n = matrix[0 ].size (); int left = 0 , right = m * n - 1 ; while (left <= right){ int mid = left + (right - left) / 2 ; if (matrix[mid / n][mid % n] > target){ right = mid - 1 ; }else if (matrix[mid / n][mid % n] < target){ left = mid + 1 ; }else { return true ; } } return false ; } };
(5) 题目: 在传递给函数之前,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)
的算法解决此问题。33. 搜索旋转排序数组 - 力扣(LeetCode)
思路: 在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r] 哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:
如果 [l, mid - 1] 是有序数组,且 target 的大小满足 [nums[l],nums[mid]),则我们应该将搜索范围缩小至 [l, mid - 1],否则在 [mid + 1, r] 中寻找。
如果 [mid, r] 是有序数组,且 target 的大小满足 (nums[mid+1],nums[r]],则我们应该将搜索范围缩小至 [mid + 1, r],否则在 [l, mid - 1] 中寻找。
PS:一定要注意等号的问题。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution : def search (self, nums: List [int ], target: int ) -> int : l, r = 0 , len (nums) - 1 while l <= r: mid = l + (r - l) // 2 if nums[mid] == target: return mid elif nums[l] <= nums[mid]: if nums[l] <= target and target < nums[mid]: r = mid - 1 else : l = mid + 1 else : if nums[mid] < target and target <= nums[r]: l = mid + 1 else : r = mid - 1 return -1
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : int search (vector<int >& nums, int target) { int l = 0 , r = nums.size () - 1 ; while (l <= r){ int mid = l + (r - l) / 2 ; if (nums[mid] == target) return mid; else if (nums[l] <= nums[mid]){ if (nums[l] <= target && target < nums[mid]) r = mid - 1 ; else l = mid + 1 ; }else { if (nums[mid] < target && target <= nums[r]) l = mid + 1 ; else r = mid - 1 ; } } return -1 ; } };