1. 停止条件
停止条件决定何时退出二分查找循环。在大多数二分查找中,循环的停止条件是通过比较左右边界来决定的。
-
常见的停止条件:
l <= r
(通常用于查找目标值是否存在,典型的查找形式):循环会在l
和r
相遇或r
小于l
时结束。l < r
(用于查找满足特定条件的边界,如找到某个值的第一个或最后一个出现位置):此条件用于确保在退出时能够保留最后一个检查的元素,适合寻找边界位置。
-
示例:
- 查找目标值时,我们通常使用
l <= r
。 - 查找某个值的边界时(如第一个或最后一个出现位置),通常使用
l < r
,这样可以避免在跳出循环后还需要多一步判断。
- 查找目标值时,我们通常使用
2. 移动条件
移动条件是根据中间值 mid
的比较结果决定的,分为两种情况:
- 向左移动:即缩小右边界
r
。 - 向右移动:即增大左边界
l
。
常见移动条件
-
标准二分查找(寻找目标值):
if nums[mid] < target
:目标值在右侧,左边界右移l = mid + 1
。if nums[mid] > target
:目标值在左侧,右边界左移r = mid - 1
。
在这种查找中,如果
nums[mid] == target
,通常会直接返回或进行其他处理。 -
寻找左边界(第一个等于目标值的位置):
- 当
nums[mid] == target
时,我们不应该立即返回,而是将右边界左移r = mid
,因为我们希望继续在左半部分查找,直到找到第一个目标值。 if nums[mid] >= target
:缩小右边界r = mid
,使得搜索范围向左靠拢。if nums[mid] < target
:左边界右移l = mid + 1
。
- 当
-
寻找右边界(最后一个等于目标值的位置):
- 当
nums[mid] == target
时,不应该立即返回,而是继续向右查找,移动左边界l = mid + 1
,以找到最后一个目标值。 if nums[mid] > target
:右边界左移r = mid - 1
。if nums[mid] <= target
:左边界右移l = mid + 1
。
- 当
3. 代码示例
让我们通过代码说明这些不同情况的设计:
3.1. 标准二分查找(查找目标值)
func binarySearch(nums []int, target int) int {
l, r := 0, len(nums) - 1
for l <= r {
mid := l + (r - l) / 2
if nums[mid] == target {
return mid
} else if nums[mid] < target {
l = mid + 1
} else {
r = mid - 1
}
}
return -1 // 如果没找到目标值
}
- 停止条件:
l <= r
,意味着当左右边界相等或交错时停止。 - 移动条件:根据
nums[mid]
和target
的比较结果,移动l
或r
。
3.2. 查找第一个等于目标值的位置
func findFirst(nums []int, target int) int {
l, r := 0, len(nums) - 1
for l < r { // 注意是 l < r
mid := l + (r - l) / 2
if nums[mid] >= target { // 尽量向左缩小范围
r = mid
} else {
l = mid + 1
}
}
if nums[l] == target {
return l
}
return -1 // 如果没找到目标值
}
- 停止条件:
l < r
,确保最后一个可能的候选位置还在范围内。 - 移动条件:当
nums[mid] >= target
时,缩小右边界,确保找到第一个目标值。
3.3. 查找最后一个等于目标值的位置
func findLast(nums []int, target int) int {
l, r := 0, len(nums) - 1
for l < r {
mid := l + (r - l) / 2
if nums[mid] > target { // 缩小右边界
r = mid - 1
} else {
l = mid + 1 // 向右寻找
}
}
if nums[l-1] == target { // 退出循环后 l-1 是最后一个等于 target 的位置
return l - 1
}
return -1 // 如果没找到目标值
}
- 停止条件:
l < r
,以确保能够查找范围的最后一个位置。 - 移动条件:当
nums[mid] <= target
时,移动左边界,继续查找最后一个目标值。
总结
- 停止条件:根据你需要的结果选择使用
l <= r
或l < r
。l <= r
:通常用于简单查找。l < r
:通常用于查找边界,如寻找第一个或最后一个等于目标值的位置。
- 移动条件:根据你的查找目的来调整移动逻辑。
- 查找目标值:移动逻辑直接根据与目标值的比较。
- 查找边界:移动逻辑需要确保不断缩小边界,直到找到最左或最右的位置。
通过这些设计,你可以根据具体需求灵活调整二分查找的停止条件和移动条件,从而高效、准确地完成查找任务。