常见二分查找模板与分类
🔁 模板一:标准二分查找
精确查找等于目标值
1
2
3
4
5
6
7
8
9
10
11
12
13
|
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func search(nums []int, target int) int {
left, right := 0, len(nums) - 1
for left <= right {
mid := left +(right - left)/2
if nums[mid] == target {
return mid
} else if nums[mid] > target {
right = mid - 1
} else {
left = mid + 1
}
}
return -1
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func isPerfectSquare(num int) bool {
if num == 1 {
return true
}
left, right := 1, num/2
for left <= right {
mid := left +(right-left)/2
if mid*mid==num {
return true
} else if mid * mid > num {
right = mid - 1
} else {
left = mid + 1
}
}
return false
}
|
🔁 模板二:二分查找下界问题
查找左边界,查找大于等于给定值的第一个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
|
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] >= target {
res = mid
right = mid - 1
} else {
left = mid + 1
}
}
return res
|
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
35
36
37
38
39
40
41
|
func searchRange(nums []int, target int) []int {
left := findLeft(nums, target)
right := findRight(nums, target)
return []int{left, right}
}
// 找左边界(第一个等于 target 的位置)
func findLeft(nums []int, target int) int {
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] < target {
left = mid + 1
} else if nums[mid] > target {
right = mid - 1
} else if nums[mid] == target {
res = mid
right = mid - 1
}
}
return res
}
// 找右边界(最后一个等于 target 的位置)
func findRight(nums []int, target int) int {
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] > target {
right = mid - 1
} else if nums[mid] < target {
left = mid + 1
} else if nums[mid] == target {
res = mid
left = mid + 1
}
}
return res
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
} else if nums[mid] > target {
right = mid - 1
} else {
left = mid + 1
}
}
return left
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func firstBadVersion(n int) int {
left, right := 1, n
var res int
for left <= right {
mid := left + (right-left)/2
if isBadVersion(mid) {
res = mid
right = mid - 1
} else {
left = mid + 1
}
}
return res
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func nextGreatestLetter(letters []byte, target byte) byte {
left, right := 0, len(letters)-1
for left <= right {
mid := left + (right-left)/2
if letters[mid] <= target {
left = mid + 1
} else {
right = mid - 1
}
}
if left == len(letters) {
return letters[0]
}
return letters[left]
}
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func missingElement(nums []int, k int) int {
// 表示到第 i 个位置缺失了多少个数
miss := func(i int) int {
return nums[i] - nums[0] - i
}
left, right := 0, len(nums)-1
for left <= right {
mid := (left + right) / 2
if miss(mid) < k {
left = mid + 1
} else {
right = mid - 1
}
}
return nums[left-1] + k - miss(left-1)
}
|
🔁 模板三:二分查找上界问题
查找有边界,查找小于等于给定值的最后一个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
|
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] <= target {
res = mid
left = mid + 1
} else {
right = mid - 1
}
}
return res
|
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
35
36
37
38
39
40
41
|
func searchRange(nums []int, target int) []int {
left := findLeft(nums, target)
right := findRight(nums, target)
return []int{left, right}
}
// 找左边界(第一个等于 target 的位置)
func findLeft(nums []int, target int) int {
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] < target {
left = mid + 1
} else if nums[mid] > target {
right = mid - 1
} else if nums[mid] == target {
res = mid
right = mid - 1
}
}
return res
}
// 找右边界(最后一个等于 target 的位置)
func findRight(nums []int, target int) int {
left, right := 0, len(nums)-1
res := -1
for left <= right {
mid := left + (right-left)/2
if nums[mid] > target {
right = mid - 1
} else if nums[mid] < target {
left = mid + 1
} else if nums[mid] == target {
res = mid
left = mid + 1
}
}
return res
}
|
sqrt(x) x 的平方根
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func mySqrt(x int) int {
if x == 0 {
return 0
}
left, right := 1, x
var res int
for left <= right {
mid := left + (right-left)/2
if mid <= x/mid {
res = mid
left = mid + 1
} else {
right = mid - 1
}
}
return res
}
|
我们要找最大的 k
,使得前 k
行的硬币数之和不超过 n
:1 + 2 + 3 + … + k = k * (k + 1) / 2 <= n
所以题目可以转化为:给定 n,求满足 k(k+1)/2 ≤ n
的最大整数 k。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func arrangeCoins(n int) int {
left, right := 0, n
for left <= right {
mid := (left + right) / 2
sum := mid * (mid + 1) / 2
if sum == n {
return mid
} else if sum < n {
left = mid + 1
} else {
right = mid - 1
}
}
return right
}
|
🔁 模板四:旋转排序数组二分查找
有序数组被部分旋转,例如:nums
原本有序,后来被旋转,例如 [4,5,6,7,0,1,2]
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
|
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
if nums[left] <= nums[mid] {
// 左边有序
if nums[left] <= target && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else {
// 右边有序
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1
|

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
|
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
if nums[mid] > nums[right] {
if target >= nums[left] && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else if nums[mid] < nums[right] {
if target > nums[mid] && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
} else {
right--
}
}
return -1
}
|
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
|
func search(nums []int, target int) bool {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return true
}
if nums[mid] > nums[right] {
// 左半部分有序
if target >= nums[left] && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else if nums[mid] < nums[right] {
// 右半部分有序
if target > nums[mid] && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
} else {
right--
}
}
return false
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func findMin(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
if nums[mid] > nums[right] {
left = mid + 1
} else if nums[mid] < nums[right] {
right = mid
} else {
right--
}
}
return nums[left]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func findMin(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
// 如果中间值大于右边值,最小值在右边
if nums[mid] > nums[right] {
left = mid + 1
} else if nums[mid] < nums[right] {
right = mid
} else { // nums[mid] == nums[right] 时不能确定,缩小右边界
right--
}
}
return nums[left]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func inventoryManagement(nums []int) int {
left, right := 0, len(nums) - 1
for left < right {
mid := left + (right - left) / 2
if nums[mid] > nums[right] {
left = mid + 1
} else if nums[mid] < nums[right] {
right = mid
} else {
right--
}
}
return nums[left]
}
|
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
|
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
res := -1
for left <= right {
if nums[left] == target {
return left
}
mid := left + (right-left)/2
if nums[mid] == target {
res = mid
right = mid - 1
} else if nums[mid] > nums[right] {
if nums[left] <= target && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else if nums[mid] < nums[right] {
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
} else {
right--
}
}
return res
}
|
🔁 模板五:二维矩阵二分查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
func searchMatrix(matrix [][]int, target int) bool {
if len(matrix) == 0 || len(matrix[0]) == 0 {
return false
}
m, n := len(matrix), len(matrix[0])
left, right := 0, m*n-1
for left <= right {
mid := left + (right-left)/2
row := mid / n // 计算对应的行
col := mid % n // 计算对应的列
if matrix[row][col] == target {
return true
} else if matrix[row][col] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return false
}
|
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
|
func kthSmallest(matrix [][]int, k int) int {
n := len(matrix)
left, right := matrix[0][0], matrix[n-1][n-1]
for left < right {
mid := left + (right-left)/2
count := 0
j := n - 1
for i := 0; i < n; i++ {
for j >= 0 && matrix[i][j] > mid {
j--
}
count += j + 1
}
if count < k {
left = mid + 1
} else {
right = mid
}
}
return left
}
|
🔁 模板六:峰值二分
寻找数组中的最小值/峰值
1
2
3
4
5
6
7
8
9
10
11
12
|
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
if nums[mid] > nums[mid+1] {
// 局部递减,峰值在左侧(含 mid)
right = mid
} else {
// 局部递增,峰值在右侧
left = mid + 1
}
}
return left
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func findPeakElement(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := (left + right) / 2
// 如果中间比右边小,说明右边有峰值
if nums[mid] < nums[mid+1] {
left = mid + 1
} else {
// 否则左边有峰值,或 mid 自己就是峰值
right = mid
}
}
// 最终 left == right,返回任意一个峰值下标
return left
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
func peakIndexInMountainArray(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
if nums[mid] > nums[mid+1] {
right = mid
} else {
left = mid + 1
}
}
return left
}
|
其他经典题
百度面试题-有序数组中绝对值最小的元素

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
|
func findMinAbs(nums []int) int {
n := len(nums)
if nums[0] >= 0 {
return nums[0] // 所有元素非负,最小绝对值为第一个
}
if nums[n-1] <= 0 {
return nums[n-1] // 所有元素非正,最小绝对值为最后一个
}
left, right := 0, n-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == 0 {
return 0 // 绝对值最小就是 0
} else if nums[mid] < 0 {
left = mid + 1
} else {
right = mid - 1
}
}
// 此时 left 指向第一个正数,right 指向最后一个负数
if abs(nums[left]) < abs(nums[right]) {
return nums[left]
}
return nums[right]
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
|
-
时间复杂度:O(log n)
-
空间复杂度:O(1)
1
2
3
4
5
6
7
|
func singleNonDuplicate(nums []int) int {
res := 0
for _, num := range nums {
res ^= num
}
return res
}
|
版权声明:
本博客所有文章除特别声明外,均采用
CC BY-NC-SA 4.0
许可协议,转载请注明出处!