Open junhong7 opened 4 years ago
mid = (start + end + 1)//2
: mid偏右侧,若a[mid] > target
则end = mid - 1
mid = o
时,start = mid + 1
跳过符合条件的值mid = (start + end)//2
: mid偏左侧,若a[mid] < target
则start = mid + 1
while start < end:
mid = (start + end + 1)//2 # 需要找第一个小于的值,所以希望调整end的位置来向左逼近
# mid偏右,每次需要调整end左移,而start = mid可以保证不会跳过小于目标的值
# 最终通过右侧逼近的方式,可以收敛在第一个小于等于target的index上
value, prev_stamp = alist[mid]
if prev_stamp > timestamp:
end = mid - 1 # 右边界向左收敛
else:
start = mid # a[mid] <= target,若mid+1则有可能跳过最后一个符合条件的最大值
# 为了start = mid不会停留在原地不更新,决定了mid = (start+end+1)/2 ,否则会infinite loop
return alist[start][0] if alist[start][1] <= timestamp else ''
O(logn)+O(log(n-1)) + ... + O(log1)
O(log(n*(n-1)*(n-2)**(1))) = O(log(n!)) <= O(nlogn) = O(logn^n)
def search(self,matrix, target, start, horizon):
low = start
high = len(matrix[0])-1 if horizon else len(matrix) - 1
# we don't need to find the index, all we care is the existence
# if we found than return Ture
# otherwise, we keep BS until the low>high to make sure that we didn't miss anything
while low <= high:
mid = low + (high - low)//2
if horizon:
if matrix[start][mid] < target:
low = mid + 1
elif matrix[start][mid] > target:
high = mid - 1
else:
return True
else:
if matrix[mid][start] < target:
low = mid + 1
elif matrix[mid][start] > target:
high = mid - 1
else:
return True
return False
def searchMatrix(self, matrix, target): """ :type matrix: List[List[int]] :type target: int :rtype: bool """ if not matrix: return False
# each time, from [start][start], we use BS to search inside the row and col O(log(len-start))
# the whole time complexity is O(logn)+O(log(n-1)) + ... + O(log1)
# T = O(log(n*(n-1)*(n-2)**(1))) = O(log(n!)) <= O(nlogn) = O(logn^n)
for i in range(min(len(matrix), len(matrix[0]))):
horizonResult = self.search(matrix, target, i, True)
verticalResult = self.search(matrix, target, i, False)
if horizonResult or verticalResult:
return True
return False
#### Solution2: Divide and Conquer
- DC涉及到递归**Recursion**,所以当矩阵规模很大时,**容易内存溢出**,所以尽量避免使用Recursion
- 因为矩阵有序,所以如果确定了其中一个值,那么左上角的矩阵元素都比他小,右下角的有比他大
- 利用这个特性,确定中间一列为`mid = (left + right)//2`,之后遍历`row from top to bottom`
- 当`matrix[row−1][mid] < target < matrix[row][mid]`的时候,即可排除左上角和右下角的两个矩阵
- 在左下和右上两个子矩阵内继续搜索,直到`subMatrix[row][mid] == target`,否则矩阵内不存在`target`
- **Time complexity : O(nlgn)**
```Python
def searchMatrix(self, matrix, target):
# an empty matrix obviously does not contain `target`
if not matrix:
return False
def search_rec(left, up, right, down):
# this submatrix has no height or no width.
if left > right or up > down:
return False
# `target` is already larger than the largest element or smaller
# than the smallest element in this submatrix.
elif target < matrix[up][left] or target > matrix[down][right]:
return False
mid = left + (right-left)//2
# Locate `row` such that matrix[row-1][mid] < target < matrix[row][mid]
row = up
while row <= down and matrix[row][mid] <= target:
if matrix[row][mid] == target:
return True
row += 1
return search_rec(left, row, mid-1, down) or search_rec(mid+1, up, right, row-1)
return search_rec(0, 0, len(matrix[0])-1, len(matrix)-1)
target > matrix[row][col]
则只需要向右侧移动一格,如果target < matrix[row][col]
则只能向上移动时间复杂度,由于最坏情况为一直走到右上角,所以总路径长度为m+n,即O(m+n)
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) == 0 or len(matrix[0]) == 0:
return False
width = len(matrix[0])
height = len(matrix)
row = height - 1
col = 0
while col < width and row >= 0:
if target == matrix[row][col]:
return True
elif target > matrix[row][col]:
col += 1
else:
row -= 1
return False
主要题型
给定数组,寻找某个数第一次出现/第n次出现/最后一次出现的下标
时间复杂度
Recursion or While loop?
面试中是否使用 Recursion 的几个判断条件
递归的危险:StackOverflow,内存消耗极大 记住:不要自己下判断,要跟面试官讨论!
Binary Search 的常见痛点
mid = start + (end - start)/2
相较于mid = (start + end)/2
的优势在于,如果start和end过大,有可能造成越界。while(start < end)
的结束条件是start == end
, 而start + 1 < end
则为两个指针相邻时结束,可以有效避免死循环。因为mid偏左,如果
a[mid] == target
,start = mid
时,有可能一轮检测下来,start不会发生移动 例如寻找[1,1]
中最后一次1出现的位置时,mid = start = 0
,start
停留在原地 解决思路就是修改循环结束的判断条件,将start < end
改为start + 1< end
关键点总结
start + 1 < end
避免死循环mid = start + (end - start)/2
避免内存越界<
,==
,>
三种情况分类讨论,即使有可能存在<=
,>=
为一类处理模式,但是分开讨论可以避免细节错误BS直观题目:寻找满足条件的的第一个/最后一个位置
1. 寻找ooooo_ox_xxxxxx
2. 特别类型:如果数组长度特别长,无法获得终点的长度,如何实现BS寻找目标?时间复杂度如何分析?
count = count * 2
跳跃式前进3. Rotated Sorted Array
a[0]
ora[-1]
?a[0]
做判断则无法进行BS非直观题目(half half):无法轻易找到ooxx条件进行二分判断
二分的本质思考:利用一次判断,缩小一半搜索的内容
mid >= start
或mid <= a[-1]
可以确认mid在前序还是后序序列mid
和target
的大小关系,进而缩小搜索区间