2.白银挑战——二分查找与搜索树高频问题
1.基于二分查找的拓展问题
1.1山脉数组的峰顶索引
LeetCode852:数组中的某位位置i开始,从0到i是递增的,从i+1到数组最后是递减的,让你找到这个最高点。
当我们遍历到下标i时,如果有arr[i-1]<arr[i]>arr[i+1],那么i就是我们需要找出的下标。
从左开始找,开始的时候必然时arr[i-1]<a[i],所以只要找到第一个arr[i]>arr[i+1]的位置即可。
class PeakIndexInMountainArray:
def peakIndexInMountainArray(self, arr):
n = len(arr)
ans = -1
for i in range(1, n-1):
if arr[i] > arr[i+1]:
ans = i
break
return ans
def peakIndexInMountainArray2(self, arr):
n = len(arr)
left, right, ans = 1, n-2, 0
while left <= right:
mid = (left + right) // 2
if arr[mid] > arr[mid + 1]:
ans = mid
right = mid - 1
else:
left = mid + 1
return ans
if __name__ == "__main__":
arr = [1, 2, 3, 4, 5, 6, 3, 2, 1]
peakIndexInMountainArray = PeakIndexInMountainArray()
res = peakIndexInMountainArray.peakIndexInMountainArray2(arr)
print(res)
1.2 旋转数字的最小数字
LeetCode153:已知一个长度为n的数组,预先按照升序排列,经由1到n次旋转后,得到输入数组。
第一种情况:nums[privot]<nums[high],说明nums[pivot]是最小值右侧的元素,因此可以忽略二分查找区间的右半部分。
第二种情况:nums[pivot]>nums[high],这说明nums[pivot]是最小值左侧的元素,因此我们 可以忽略二分查找区间的右半部分。
由于数组不包含重复元素,并且只要当前的区间长度不为1,pivot就不会与high重合,而如果当前的区间长度为1,这说明我们已经可以结束二分查找了。因此不会存在nums[pivot]=nums[high]的情况。
当二分查找结束时,我们就得到了最小值所在的位置。
class FindMin:
def findMin(self, nums):
low, high = 0, len(nums) - 1
while low < high:
pivot = low + (high - low) // 2
if nums[pivot] < nums[high]:
high = pivot
else:
low = pivot + 1
return nums[low]
if __name__ == "__main__":
nums = [4, 5, 6, 7, 0, 1, 2]
findMin = FindMin()
res = findMin.findMin(nums)
print(res)
1.3找缺失数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内。
在范围0-n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
因此,只需要二分找出第一个nums[i]!=i,此时下标i就是答案。若数组元素中没有找到此下标,那么缺失的就是n.
class MissingNumber:
def missingNumber(self, nums):
low, high = 0, len(nums) - 1
while low <= high:
mid = (low + high) // 2
if nums[mid] == mid:
low = mid + 1
else:
high = mid - 1
return nums[low]
if __name__ == "__main__":
nums = [0, 1, 2, 3, 4, 5, 7, 8]
missingNumber = MissingNumber()
res = missingNumber.missingNumber(nums)
print(res)
1.4优化求平方根
实现函数int sqrt(int x).计算并返回x的平方根这个题的思路是用最快的方式找到n*n=x的n,所以采用折半进行比较。
class MySqrt:
def mySqrt(self, x):
left, right, ans = 0, x, -1
while left <= right:
mid = (left + right) // 2
if mid * mid <= x:
ans = mid
left = mid + 1
else:
right = mid - 1
return ans
if __name__ == "__main__":
x = 81
mySqrt = MySqrt()
res = mySqrt.mySqrt(x)
print(res)
凡是在有序区间查找的场景,都可以用二分查找来优化速度。
如果有序空间是变化的,那就每次都针对这个变化的区间进行二分查找
2.中序与搜索树原理
二叉搜索树:一颗二叉树是搜索树,则按照中序遍历其序列正好是一个递增序列。比较规范的定义是:
若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值
若它的右子树不空,则右子树上所有节点的值均大于他的根节点的值
他的左右子树也分别为二叉排序树
2.1二叉搜索树中搜索特定值
LeetCode700:给定二叉搜索树(BST)的根节点和一个值。在BST中找到节点值等于给定值的节点。返回以该节点为根的子树。如果节点不存在,则返回Null
class Node:
def __init__(self, val=-1, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Tree:
def __init__(self, root=None):
self.root = root
def init_tree_for_search_bst(self):
node_1 = Node(1)
node_3 = Node(3)
node_2 = Node(2, node_1, node_3)
node_7 = Node(7)
root = Node(4, node_2, node_7)
return root
class SearchBST:
def searchBST(self, root, val):
if root is None:
return None
if val == root.val:
return root
return self.searchBST(root.left if val < root.val else root.right, val)
# 迭代方式
def searchBST2(self, root, val):
while root:
if root.val == val:
return root
return root.left if val < root.val else root.right
return None
if __name__ == "__main__":
tree = Tree()
root = tree.init_tree_for_search_bst()
searchBST = SearchBST()
res = searchBST.searchBST2(root, 2)
print(res)