二分查找模板
以下情况只考虑下标从0开始
参考链接https://leetcode.cn/leetbook/read/binary-search/xewjg7/
这里的模板2中,也可以写成right=length-1
模板1
最简单的二分搜索模板,不需要找第一个、最后一个位置,也没有重复元素,可以使用这个模板
注意1,跳出循环有两种情况
- 直接mid就是答案,此时l和r无规律
- 没有找到答案,此时一定
right+1==left
注意2,由于循环退出条件是l<=r,也就是说当l==r时是不会退出循环的,因此不能出现l=mid,也不能出现r=mid
比如最后缩小到[1,1]区间时,此时l=1,r=1,mid=1,如果l=mid,或r=mid,则不会退出循环
func search(arr []int, value int) int {
l, r := 0, len(arr)-1
for l <= r {
mid := l + (r-l)/2
if arr[mid] == value {
return mid
} else if arr[mid] < value {
l = mid + 1
} else {
r = mid - 1
}
}
//r+1 == l
return -1
}
func main() {
var arr []int = []int{1, 2, 3, 4, 5}
val := 3
fmt.Println(search(arr, val))
}
浮点数模板
Pie
https://vjudge.net/problem/POJ-3122
func check(n, f int, cur float64, piArray []float64) bool {
var sum = 0
for i := 0; i < n; i++ {
sum += int(math.Floor(piArray[i] / cur))
}
if sum >= f+1 {
return true
} else {
return false
}
}
func main() {
var pi float64 = math.Pi
var eps float64 = 1e-6
var r int
var n, f int
fmt.Scan(&r)
for {
if r -= 1; r == -1 {
break
}
fmt.Scan(&n, &f)
piArray := make([]float64, n)
left, right, mid := 0.0, 0.0, 0.0
for i := 0; i < n; i++ {
fmt.Scan(&piArray[i])
piArray[i] = pi * piArray[i] * piArray[i]
right = math.Max(right, piArray[i])
}
for math.Abs((right - left)) >= eps {
mid = left + (right-left)/2
if check(n, f, mid, piArray) {
left = mid
} else {
right = mid
}
}
fmt.Printf("%.4f\n", left)
}
}
模板2
注意1,跳出循环时,一定有left==right
注意2,循环里不能同时出现left=mid和right=mid,只能出现其中一个
比如最后缩小到[1,2]区间时,此时l=1,r=2,mid=1
- 如果mid是向下取整,
mid=left+ (right-left)/2,则判断后不能出现left=mid,否则会死循环 - 如果mid是向上取整,
mid=left+(right-left+1)/2,则判断后不能出现right=mid,否则会死循环
模板3
搜索条件需要访问元素的直接左右两个邻居
使用元素的左右两个邻居来确定是向左还是向右
保证查找空间在每个步骤中至少有3个元素
需要进行后续处理,退出查找后,一定有left+1==right,这个区间有两个元素,因此这里是left=mid, right=mid
例题
35.搜索插入位置
https://leetcode.cn/problems/search-insert-position/description/
题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
//模板1
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right{
mid := left + (right-left)/2
if (target < nums[mid]){
right = mid - 1
}else if (target > nums[mid]){
left = mid +1
}else {
return mid
}
}
return left
}
//模板2
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{
left = mid+1
}else{
right = mid
}
}
if nums[left]>target{
return left
}else{
return left+1
}
}
163.寻找峰值
https://leetcode.cn/problems/find-peak-element/description/
题目描述
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
//模板3
func findPeakElement(nums []int) int {
left, right := 0, len(nums)-1
for left+1 < right{
mid := left + (right-left)/2
if nums[mid-1] < nums[mid]{
left = mid
}else{
right = mid
}
}
if nums[left] > nums[right]{
return left
}else{
return right
}
}
搜索区间
https://www.lintcode.com/problem/search-for-a-range/description
题目描述
给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置。
如果目标值不在数组中,则返回[-1, -1]
即给定一个数,查找其在数组中第一个和最后一个出现的下标
//模板3
func SearchRange(arr []int, target int) []int {
if len(arr) == 0{
return []int{-1, -1}
}
result := make([]int, 2)
//第一次出现的下标
left, right := 0, len(arr)-1
for left+1 < right{
mid := left + (right-left)/2
if arr[mid] >= target{ //相等的话就往左找
right=mid
}else {
left =mid
}
}
if arr[left] == target{
result[0] = left
}else if arr[right] == target{
result[0] = right
}else{
result[0], result[1] = -1, -1
return result
}
//最后一次出现的下标
left, right = 0, len(arr)-1
for left + 1 < right{
mid := left + (right-left)/2
if arr[mid]<=target{//相等的话就往右找
left = mid
}else{
right = mid
}
}
if arr[right] == target{
result[1] = right
}else if arr[left] == target{
result[1] = left
}
return result
}
牛牛分蛋糕
https://www.nowcoder.com/practice/435b7ac0d7eb4927a0ce4ef2ffcc1385
题目描述
牛牛今天家里要来客人,所以牛牛今天特意做了他最拿手的两种蛋糕,但是他是一个有洁癖的人,所以他在分蛋糕时,有如下几个原则:
1.他不希望一个盘子里出现两种蛋糕
2.他希望每个盘子中都有蛋糕
3.他想让装有最少蛋糕数量的盘子中装有的蛋糕数量尽可能多
//模板1
func check(n, a, b, cur int) bool {
if a/cur+b/cur >= n {
return true
} else {
return false
}
}
func splitCake(n int, a int, b int) int {
left, right := 1, int(math.Min(float64(a), float64(b)))
var ans int
for left <= right {
mid := left + (right-left)/2
if check(n, a, b, mid) {
left = mid + 1
ans = mid
} else {
right = mid - 1
}
}
return ans
}
//模板2
func check(n, a, b, cur int) bool {
if a/cur+b/cur >= n {
return true
} else {
return false
}
}
func splitCake(n int, a int, b int) int {
left, right := 1, int(math.Min(float64(a), float64(b)))
for left < right {
mid := left + (right-left+1)/2 //注意这里要找可行域的有边界,所以这里需要向上取整,防止死循环
if check(n, a, b, mid) {
left = mid
} else {
right = mid - 1
}
}
return left
}
//模板3
func check(n, a, b, cur int) bool {
if a/cur+b/cur >= n {
return true
} else {
return false
}
}
func splitCake(n int, a int, b int) int {
left, right := 1, int(math.Min(float64(a), float64(b)))
for left+1 < right {
mid := left + (right-left)/2
if check(n, a, b, mid) {
left = mid
} else {
right = mid
}
}
if check(n, a, b, right) {
return right
} else {
return left
}
}
三分模板
二分查找适用于单调性(包括非严格单调)的函数或数组
如果一个数组序列或函数不单调,有一个拐点的话,需要使用三分查找
三分查找模板https://blog.csdn.net/weixin_42172261/article/details/88926415