Given a sorted array of n elements, write a function to search a given element x in array.
- Search算法里的入门算法,其实很多高级算法的思路都是以二分查找为基本思想引出的,特别是那些和logn相关的算法,比如说自平衡二叉树
经典二分查找
- 有序, 数组
- Why 链表不可以?Why 无序不可以?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution: def findPosition(self, nums, target): if not nums: return -1
start, end = 0, len(nums)-1
while start <= end: mid = start + (end - start) // 2
if nums[mid] == target: return mid elif nums[mid] > target: end = mid - 1 else: start = mid + 1 return -1
|
- 一维Array和二维Matrix的转换
- index => (x, y)
- x = index / col
- y = index % col
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution(object): def searchMatrix(self, matrix, target): if not matrix: return False m, n = len(matrix), len(matrix[0]) start, end = 0, m*n-1 while start <= end: mid = start + (end - start) / 2 row, col = mid / n, mid % n if matrix[row][col] < target: start = mid + 1 elif matrix[row][col] > target: end = mid - 1 else: return True return False
|
九章算法二分查找模板
- while 循环 ?
- while start + 1 < end, 留两个值做post processing
- while start < end, 留一个值做post processing
- while start < = end, 不留值
- 缩小查找范围 ?
- start = mid 还是 start = mid + 1
- end = mid 还是 end = mid + 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| def findPosition(self, A, target): if(len(A) == 0): return -1
start = 0 end = len(A) - 1
while(start + 1 < end): mid = start + (end - start) / 2 if(A[mid] == target): return mid if(A[mid] < target): start = mid else: end = mid
if (A[start] == target): return start elif (A[end] == target): return end else: return -1
|
- Double Check 小技巧:
- 当前这个mid对应的值是不是 可能是要找的结果,mid + 1 还是 mid ?
- start = mid 一定要用 while start + 1 < end !!!
- 要找的结果是不是一定在数组当中!start < end 还是 start < = end
边界系列
- Input是标准的 Sorted Array(单调增或者单调减)
- First or Last of Occrance!
- 大于等于,小于等于,大于,小于,等于
- 每次淘汰的空间要确定是不属于结果范围的!
- 000000011111111
- Find The First Occrance of an Element
- 找的元素在不在数组里?while start < end 还是 while start < = end
- How about Last Good Version?
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
| class Solution(object): def firstBadVersion(self, n): start, end = 1, n while start < end: mid = start + (end - start) / 2 ret = isBadVersion(mid) if ret: end = mid else: start = mid + 1 return start
class Solution(object): def firstBadVersion(self, n): start, end = 1, n
while start + 1 < end: mid = start + (end - start) / 2 ret = isBadVersion(mid) if ret: end = mid else: start = mid + 1
if isBadVersion(start): return start else: return end
|
- 找 > = target 的 first occrence
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution(object): def searchInsert(self, nums, target): if not nums: return 0 if target > nums[-1]: return len(nums)
start, end = 0, len(nums)-1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] >= target: end = mid else: start = mid + 1
if nums[start] >= target: return start else: return end
|
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
| class Solution(object): def searchRange(self, nums, target): if not nums: return [-1, -1]
start, end = 0 , len(nums) - 1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] >= target: end = mid else: start = mid + 1
if nums[start] == target: first = start elif nums[end] == target: first = end else: return [-1, -1]
start, end = 0, len(nums) - 1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] <= target: start = mid else: end = mid - 1
if nums[end] == target: return [first, end] else: return [first, start]
|
旋转数组系列
- 只有Sorted Array才能做Binary Search?
- 每次淘汰的空间要确定是不属于结果范围的!
- 根据要找的Target条件缩小Search的范围!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution(object): def findPeakElement(self, nums): if len(nums) <= 1: return 0
start, end = 0 , len(nums)-1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] < nums[mid+1]: start = mid + 1 else: end = mid
if nums[start] > nums[end]: return start else: return end
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution(object): def findPeakElement(self, nums): if len(nums) <= 1: return 0
start, end = 0 , len(nums)-1 while start < end: mid = start + (end - start) / 2 if nums[mid] < nums[mid+1]: start = mid + 1 else: end = mid
return start
|
- Why nums[mid] 和 nums[end]比较?
1 2 3 4 5 6 7 8 9 10 11
| class Solution(object): def findMin(self, nums): start, end = 0, len(nums)-1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] > nums[end]: start = mid + 1 else: end = mid return min(nums[start], nums[end])
|
1 2 3 4 5 6 7 8 9 10
| class Solution(object): def findMin(self, nums): start, end = 0, len(nums)-1 while start < end: mid = start + (end - start) / 2 if nums[mid] > nums[end]: start = mid + 1 else: end = mid return nums[start]
|
- What if duplicates are allowed?
- Worst Case O(n)
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
| class Solution(object): def findMin(self, nums): start, end = 0, len(nums)-1 while start < end: mid = start + (end - start) / 2 if nums[mid] < nums[end]: end = mid elif nums[mid] > nums[end]: start = mid + 1 else: end -= 1 return nums[start]
class Solution(object): def findMin(self, nums): start, end = 0, len(nums)-1 while start + 1 < end: mid = start + (end - start) / 2 if nums[mid] < nums[end]: end = mid elif nums[mid] > nums[end]: start = mid + 1 else: end -= 1 return min(nums[start], nums[end])
|
- 1 把Array先根据nums[end]分成在前段还是后段
- 2 再根据nums[mid]的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution(object): def search(self, nums, target): start, end = 0, len(nums)-1 while start <= end: mid = start + (end - start) / 2 if nums[mid] == target: return mid if nums[mid] <= nums[end]: if nums[mid] < target <= nums[end]: start = mid + 1 else: end = mid - 1 else: if nums[start] <= target < nums[mid]: end = mid - 1 else: start = mid + 1 return -1
|
- nums may contain duplicates
- nums[mid] == nums[start] 或者 nums[mid] == nums[end]的时候无法判断nums[mid]是在前段还是后段!
- start += 1 缩减范围, Worst Case : O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution(object): def search(self, nums, target): start, end = 0, len(nums)-1 while start <= end: mid = (start + end) / 2 if nums[mid] == target: return True
if nums[mid] < nums[start]: if nums[mid] < target <= nums[end]: start = mid + 1 else: end = mid - 1 elif nums[mid] > nums[start]: if nums[start] <= target < nums[mid]: end = mid - 1 else: start = mid + 1 else: start += 1 return False
|
数值二分型
- 典型的二分法: 输入是数组,双指针分别是数组的index
- Input不再是Array,不再以index,而是以value来做二分
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution(object): def guessNumber(self, n): start, end = 1, n while start < end: mid = start + (end - start) // 2 ret = guess(mid) if ret == 0: return mid elif ret < 0: end = mid - 1 else: start = mid + 1 return start
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution(object): def isPerfectSquare(self, num): start, end = 1, num while start <= end: mid = start + (end - start) / 2 ret = mid * mid if ret > num: end = mid - 1 elif ret < num: start = mid + 1 else: return True return False
|
- 在0~x中找最大的数r,满足r * r < = x
- Why start + 1 < end ?
- 剩余的搜索范围都是满足搜索条件的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution(object): def mySqrt(self, x): start, end = 0, x while start + 1 < end: mid = start + (end - start) / 2 ret = mid * mid if ret > x: end = mid - 1 elif ret < x: start = mid else: return mid
if end * end <= x: return end else: return start
|