日常开发过程中,除了我们上篇讲到的正常的二分查找,还有很多二分查找的变形版本,今天开始,我们就来给大家一一介绍这些变形版本。
从给定序列中查找第一个匹配元素
符合标准的二分查找条件的序列一般是比较理想的情况,如果要查找的元素在序列中有多个怎么办?所以我们要给大家介绍的第一个常见的变形版本,就是在一个给定排序序列中查找第一个等于给定值的元素。在继续往下看之前,你不妨借机先思考一下要怎么实现。
midmidmid
num == nums[mid]
package main
import (
"fmt"
)
// 二分查找变形版本1:查找第一个值等于给定值的元素
func binarySearchV1(nums []int, num, low, high int) int {
if low > high {
return -1
}
mid := (low + high) / 2
if num < nums[mid] {
return binarySearchV1(nums, num, low, mid - 1)
} else if num > nums[mid] {
return binarySearchV1(nums, num, mid + 1, high)
} else {
// 除了需要判断第一个与 num 相等的元素索引外,其他和普通二分查找逻辑一致
if mid == 0 || nums[mid-1] != num {
return mid
} else {
return binarySearchV1(nums, num, low, mid - 1)
}
}
}
func main() {
nums := []int{1, 2, 3, 3, 4, 5, 6}
num := 3
index := binarySearchV1(nums, num, 0, len(nums) - 1)
if index != -1 {
fmt.Printf("Find num %d first at index %d\n", num, index)
} else {
fmt.Printf("Num %d not exists in nums\n", num)
}
}
运行上述代码,打印结果如下:
从给定序列中查找最后一个匹配元素
既然有第一个等于给定值的查询,自然就有最后一个等于给定值的查询,这就是二分查找的第二个变形版本:在给定已排序序列中查找最后一个等于给定值的元素。
num == nums[mid]midmidmid
下面我来给出查找最后一个等于给定值的元素的 Go 实现代码:
package main
import "fmt"
// 二分查找变形版本2:查找最后一个值等于给定值的元素
func binarySearchV2(nums []int, num, low, high int) int {
if low > high {
return -1
}
mid := (low + high) / 2
if num < nums[mid] {
return binarySearchV2(nums, num, low, mid - 1)
} else if num > nums[mid] {
return binarySearchV2(nums, num, mid + 1, high)
} else {
// 除了需要判断最后一个与 num 相等的元素索引外,其他和普通二分查找逻辑一致
if mid == len(nums) - 1 || nums[mid + 1] != num {
return mid
} else {
return binarySearchV2(nums, num, mid + 1, high)
}
}
}
func main() {
nums := []int{1, 2, 3, 3, 4, 5, 6}
num := 3
index := binarySearchV2(nums, num, 0, len(nums) - 1)
if index != -1 {
fmt.Printf("Find num %d last at index %d\n", num, index)
} else {
fmt.Printf("Num %d not exists in nums\n", num)
}
}
运行上述代码,打印结果如下:
在给定序列中查找第一个大于等于给定值的元素
二分查找的第三个变形版本是:在给定排序序列中查找第一个大于等于给定值的元素。
nums[mid] >= nummidmidmid
对应的 Go 实现代码如下:
package main
import "fmt"
// 二分查找变形版本3:查找第一个大于等于给定值的元素
func binarySearchV3(nums []int, num, low, high int) int {
if low > high {
return -1
}
mid := (low + high) / 2
if num <= nums[mid] {
// 判断条件变更,找到第一个大于等于给定值的元素
// 最左侧元素值,或者当前 mid 位置前一个元素值小于给定值
if mid == 0 || nums[mid-1] < num {
return mid
} else {
return binarySearchV3(nums, num, low, mid-1)
}
} else {
return binarySearchV3(nums, num, mid+1, high)
}
}
func main() {
nums := []int{1, 2, 3, 3, 4, 5, 6}
num := 3
index := binarySearchV3(nums, num, 0, len(nums) - 1)
if index != -1 {
fmt.Printf("Find first num greater or equal than %d at index %d\n", num, index)
} else {
fmt.Printf("Num %d not exists in nums\n", num)
}
}
运行上述代码,打印结果如下:
在给定序列中查找最后一个小于等于给定值的元素
与之相对的,还有我们最后要讨论的一个二分查找的变形版本:在给定序列中查找最后一个小于等于给定值的元素。
nums[mid] <= numnummidmidnummid
对应的 Go 实现代码如下:
package main
import "fmt"
// 二分查找变形版本4:查找最后一个小于等于给定值的元素
func binarySearchV4(nums []int, num, low, high int) int {
if low > high {
return -1
}
mid := (low + high) / 2
if num >= nums[mid] {
// 判断条件变更,找到最后一个小于等于给定值的元素
// 最右侧元素值,或者当前 mid 位置后一个元素值大于给定值
if mid == len(nums) - 1 || nums[mid + 1] > num {
return mid
} else {
return binarySearchV4(nums, num, mid + 1, high)
}
} else {
return binarySearchV4(nums, num, low, mid - 1)
}
}
func main() {
nums:= []int{1, 2, 3, 3, 4, 5, 6}
num := 3
index := binarySearchV4(nums, num, 0, len(nums) - 1)
if index != -1 {
fmt.Printf("Find last num less or equal than %d at index %d\n", num, index)
} else {
fmt.Printf("Num %d not exists in nums\n", num)
}
}
运行上述代码,打印结果如下:
关于二分查找及其变形版本,学院君就简单介绍到这里,所有教程代码,可以在 Github 代码仓库获取:nonfu/go-tutorial,接下来,我们将开始介绍常见的字符串匹配算法。
(本文完)