二分查找有很多细节要注意。
对于 x = 8,它的开方是 2.82842…,最后应该返回 2 而不是 3。在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。
解法一:
class Solution:
def mySqrt(self, x: int) -> int:
# 直接返回的情况
if x <= 1:
return x
# 不是index了,而是找1~x中根号下x的整数值。所以low、high初始化为1,x
l, h = 1, x
while l <= h:
# 整数
mid = (l + h) >> 1
# sqrt取整数部分
sqrt = x // mid
if sqrt == mid:
return mid
elif mid > sqrt:
h = mid - 1
else:
l = mid + 1
return h
解法二:
class Solution:
def mySqrt(self, x: int) -> int:
l, r, ans = 0, x, -1
while l <= r:
mid = (l + r) // 2
if mid * mid <= x:
ans = mid
l = mid + 1
else:
r = mid - 1
return ans
给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
如果目标字母 target = ‘z’ 并且字符列表为 letters = [‘a’, ‘b’],则答案返回 ‘a’
寻找有序列表里比目标字母大的最小字母,相当于寻找右边界,左指针指向的元素。若target存在在letters中,那么返回左指针指向的元素;若target不存在在letters中 (那么left指针会停在index = length, right指针会停在left - 1即index = length - 1), 要么是比letters数组中的字母都小,要么是都大,这两种情况都可以返回letters[0]。
class Solution:
def nextGreatestLetter(self, letters: List[str], target: str) -> str:
length = len(letters)
left = 0
right = length-1
while(left<=right):
mid = (left+right)//2
if letters[mid]>target:
right = mid-1
else:
left = mid+1
if left == length:
return letters[0]
else:
return letters[left]
https://leetcode.com/problems/single-element-in-a-sorted-array/discuss/100732/Short-compare-numsi-with-numsi1
Simply find the first index whose “partner index” (the index xor 1) holds a different value. Every 2 numbers are partner. (even,odd), (even,odd). If mid is even, it’s partner is next odd, if mid is odd, it’s partner is previous even. odd xor 1 = odd-1 even xor 1 = even+1
自己调试一遍,更清晰!
def singleNonDuplicate(self, nums):
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) / 2
if nums[mid] == nums[mid ^ 1]:
lo = mid + 1
else:
hi = mid
return nums[lo]
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return reduce(lambda x, y: x ^ y, nums)
reduce函数的用法:
reduce(function, iterable[, initializer])
lambda函数的用法:
函数名 = lambda 输入:返回值(输出),一般为输入的表达式
相当于找到一个边界,这里不分上边界or下边界,所以也没有nums[mid] >= target (找下边界)还是nums[mid] > target (找上边界),只有isBadVersion(mid) is True or False。最后找到的边界,left是第一个错误的版本,right是最后一个正确的版本。
注意初始化,这里初始化成1-n是因为index(版本号)就是1-n。
class Solution:
def firstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
# 这里初始化成1~n是因为index(版本号)就是1~n
l, r = 1, n
#这里应该是 l <= r 才能使l, r逼近同一个值
while l <= r:
mid = (l + r) // 2
if isBadVersion(mid):
#如果说该mid是错误版本,那么应该向前查找
r = mid - 1
else:
#如果说该mid是正确版本,那么应该向后查找
l = mid + 1
#最后会逼近到第一个错误版本号l
return l
1)找分界线,然后看看是返回分界线左边还是分界线右边的元素
https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/solution/
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# if 数组只有一个数
if len(nums) == 1:
return nums[0]
left, right = 0, len(nums) - 1
# if 数组有序
if nums[right] > nums[0]:
return nums[0]
# 二分查找
while left <= right:
mid = left + (right - left) // 2
# 看看分界线mid左边和右边的元素,如果符合“前高后低”,那么低的那个就是要找到min
if nums[mid] > nums[mid + 1]:
return nums[mid + 1]
if nums[mid - 1] > nums[mid]:
return nums[mid]
# 二分查找更新left和right
if nums[mid] > nums[0]:
left = mid + 1
else:
right = mid - 1
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
while left < right: # 最后停在left == right
mid = (left + right) // 2
if nums[mid] > nums[right]: # mid处元素 > right处元素,说明[mid+1, len(nums)-1]有最小值,left = mid + 1
left = mid + 1
else: # mid处元素 <= right处元素,说明[mid, len(nums)-1]是递减的,要么mid是最小值,要么最小值在mid前面
right = mid
# 直到 left == right,找到最小值!
return nums[right]
public int[] searchRange(int[] nums, int target) {
int first = findFirst(nums, target);
int last = findFirst(nums, target + 1) - 1;
if (first == nums.length || nums[first] != target) {
return new int[]{-1, -1};
} else {
return new int[]{first, Math.max(first, last)};
}
}
private int findFirst(int[] nums, int target) {
int l = 0, h = nums.length; // 注意 h 的初始值
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] >= target) {
h = m;
} else {
l = m + 1;
}
}
return l;
}
2)解法二:用两个二分查找分别找出第一个位置(下边界,返回left)和最后一个位置(上边界,返回right)
class Solution {
public:
< @note 寻找target的左边界
int leftBound(vector<int> &nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right-left)/2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
/*
* 当target小于nums中所有值时,left=0, right=-1
* 当target大于nums中所有值时,left=nums.size(), right=nums.size()-1
* 当target处于nums的最大值和最小值之间,但没有相等值,就需要第二个判断*/
if (left >= nums.size() || nums[left] != target) {
return -1;
}
return left;
}
< @note 寻找target的右边界
int rightBound(vector<int> &nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right-left)/2;
if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if (right < 0 || nums[right] != target) {
return -1;
}
return right;
}
vector<int> searchRange(vector<int>& nums, int target) {
int left = leftBound(nums, target);
int right = rightBound(nums, target);
return {left, right};
}
};
lc某题. 查找==target的第一个或最后一个元素的index,若找不到则返回-1
注意在最后返回时不是返回left(下边界,返回left,得到第一个=target的元素index),或right(上边界,返回right,得到最后一个=target的元素index),而是要写成:
return left if left < len(nums) else -1
or
return right if right >= 0 else -1