一个几乎全民都会的算法——二分查找_二分查找是目前新兴的一种流行算法(1)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

binarySearchRecursive(target, low, high, 0)

}


运行结果: 


第1次报价:1650 低了


第2次报价:1725 高了


第3次报价:1687 高了


第4次报价:1668 低了


第5次报价:1677 高了


第6次报价:1672 高了


第7次报价:1670 正确!


加上预估区间的2次,共9次猜出正确价格。为什么比人猜多了,因为人猜时默认价格是10的倍数,是没有个位数的。修改代码,也用mid+10 和 mid-10试试:



package main

import “fmt”

// 二分查找算法
func binarySearchRecursive(target int, low int, high int, count int) int {
if low > high {
return -1
}
count++
mid := low + (high-low)/2
fmt.Printf("第%v次报价:%v ", count, mid)
if mid == target {
fmt.Println(“正确!”)
return mid
} else if target < mid {
fmt.Println(“高了”)
return binarySearchRecursive(target, low, mid-10, count)
} else {
fmt.Println(“低了”)
return binarySearchRecursive(target, mid+10, high, count)
}
}

func main() {
target := 1670 //商品正确价格为1670元
low, high := 1500, 1800 //预估商品价格区间
binarySearchRecursive(target, low, high, 0)
}


运行结果: 


第1次报价:1650 低了


第2次报价:1730 高了


第3次报价:1690 高了


第4次报价:1670 正确!


虽然这个例子是猜中了,但对区间变动不是mid±1的还是谨慎使用,很可能会错失目标的。


**盲猜:**


如果不知道是什么商品,也就是估不准价格,比如我们指定是100000以内价格区间,看要猜多少次?



package main

import “fmt”

// 二分查找算法
func binarySearchRecursive(target int, low int, high int, count int) int {
if low > high {
return -1
}
count++
mid := low + (high-low)/2
fmt.Printf("第%v次报价:%v ", count, mid)
if mid == target {
fmt.Println(“正确!”)
return mid
} else if target < mid {
fmt.Println(“高了”)
return binarySearchRecursive(target, low, mid-1, count)
} else {
fmt.Println(“低了”)
return binarySearchRecursive(target, mid+1, high, count)
}
}

func main() {
target := 1670 //商品正确价格为1670元
low, high := 0, 100000 //预估商品价格区间
binarySearchRecursive(target, low, high, 0)
}


运行结果: 


第1次报价:50000 高了


第2次报价:24999 高了


第3次报价:12499 高了


第4次报价:6249 高了


第5次报价:3124 高了


第6次报价:1561 低了


第7次报价:2342 高了


第8次报价:1951 高了


第9次报价:1756 高了


第10次报价:1658 低了


第11次报价:1707 高了


第12次报价:1682 高了


第13次报价:1670 正确!


这个次数不高于log2(100000) ≈ 16.61,所以二分查找的时间复杂度为 O(log n)。




---


### 力扣实战


#### 查找元素的首末位置


Find-first-and-last-position-of-element-in-sorted-array


给定一个按照升序排列的整数数组 `nums`,和一个目标值 `target`。找出给定目标值在数组中的开始位置和结束位置。


如果数组中不存在目标值 `target`,返回 `[-1, -1]`。


**进阶:**


* 你可以设计并实现时间复杂度为 `O(log n)` 的算法解决此问题吗?


**示例 1:**



**输入:**nums = [5,7,7,8,8,10], target = 8
输出:[3,4]


**示例 2:**



**输入:**nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]


**示例 3:**



**输入:**nums = [], target = 0
输出:[-1,-1]


**提示:**


* `0 <= nums.length <= 10^5`
* `-10^9 <= nums[i] <= 10^9`
* `nums` 是一个非递减数组
* `-10^9 <= target <= 10^9`


**代码:**



package main

import “fmt”

func searchRange(nums []int, target int) []int {
left, right := -1, -1
// 查找左边界
l, r := 0, len(nums)-1
for l <= r {
mid := (l + r) / 2
if nums[mid] == target {
left = mid
r = mid - 1
} else if nums[mid] > target {
r = mid - 1
} else {
l = mid + 1
}
}
// 如果左边界没找到,直接返回
if left == -1 {
return []int{-1, -1}
}
// 查找右边界
l, r = 0, len(nums)-1
for l <= r {
mid := (l + r) / 2
if nums[mid] == target {
right = mid
l = mid + 1
} else if nums[mid] > target {
r = mid - 1
} else {
l = mid + 1
}
}
return []int{left, right}
}

func main() {

nums := []int{5, 7, 7, 8, 8, 10}
fmt.Println(searchRange(nums, 8))
fmt.Println(searchRange(nums, 6))
nums = []int{}
fmt.Println(searchRange(nums, 0))

}


#### x 的平方根  Sqrt x


给你一个非负整数 `x` ,计算并返回 `x` 的 **算术平方根** 。


由于返回类型是整数,结果只保留 **整数部分**,小数部分将被 **舍去 。**


**注意:**不允许使用任何内置指数函数和算符,例如 `pow(x, 0.5)` 或者 `x ** 0.5` 。


**示例 1:**



**输入:**x = 4
**输出:**2


**示例 2:**



**输入:**x = 8
**输出:**2
**解释:**8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。


**提示:**


* `0 <= x <= 2^31 - 1`


**代码:** 



package main

import (
“fmt”
)

func mySqrt(x int) int {
left, right := 0, x
res := -1
for left <= right {
mid := left + (right-left)/2
guess := mid * mid
if guess <= x {
res = mid
left = mid + 1
} else {
right = mid - 1
}
}
return res
}

func main() {
fmt.Println(mySqrt(4))
fmt.Println(mySqrt(8))
fmt.Println(mySqrt(122))
}


#### 寻找旋转排序数组中的最小值


Find-minimum-in-rotated-sorted-array


已知一个长度为 `n` 的数组,预先按照升序排列,经由 `1` 到 `n` 次 **旋转** 后,得到输入数组。例如,原数组 `nums = [0,1,2,4,5,6,7]` 在变化后可能得到:


* 若旋转 `4` 次,则可以得到 `[4,5,6,7,0,1,2]`
* 若旋转 `7` 次,则可以得到 `[0,1,2,4,5,6,7]`


注意,数组 `[a[0], a[1], a[2], ..., a[n-1]]` **旋转一次** 的结果为数组 `[a[n-1], a[0], a[1], a[2], ..., a[n-2]]` 。


给你一个元素值 **互不相同** 的数组 `nums` ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 **最小元素** 。


你必须设计一个时间复杂度为 `O(log n)` 的算法解决此问题。


**示例 1:**



**输入:**nums = [3,4,5,1,2]
**输出:**1
**解释:**原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。


**示例 2:**



**输入:**nums = [4,5,6,7,0,1,2]
**输出:**0
**解释:**原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。


**示例 3:**



**输入:**nums = [11,13,15,17]
**输出:**11
**解释:**原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。


**提示:**


* `n == nums.length`
* `1 <= n <= 5000`
* `-5000 <= nums[i] <= 5000`
* `nums` 中的所有整数 **互不相同**
* `nums` 原来是一个升序排序的数组,并进行了 `1` 至 `n` 次旋转


**代码:** 



package main

import “fmt”

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 {
right = mid
}
}
return nums[left]
}

func main() {
nums := []int{3, 4, 5, 1, 2}
fmt.Println(findMin(nums))
nums = []int{4, 5, 6, 7, 0, 1, 2}
fmt.Println(findMin(nums))
nums = []int{11, 13, 15, 17}
fmt.Println(findMin(nums))
}


#### 寻找峰值


Find Peak Element


峰值元素是指其值严格大于左右相邻值的元素。


给你一个整数数组 `nums`,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 **任何一个峰值** 所在位置即可。


你可以假设 `nums[-1] = nums[n] = -∞` 。


你必须实现时间复杂度为 `O(log n)`的算法来解决此问题。


**示例 1:**



**输入:**nums = [1,2,3,1]
**输出:**2
**解释:**3 是峰值元素,你的函数应该返回其索引 2。


**示例 2:**



**输入:**nums = [1,2,1,3,5,6,4]
**输出:**1 或 5
**解释:**你的函数可以返回索引 1,其峰值元素为 2;
  或者返回索引 5, 其峰值元素为 6。


**提示:**


* `1 <= nums.length <= 1000`
* `-2^31 <= nums[i] <= 2^31 - 1`
* 对于所有有效的 `i` 都有 `nums[i] != nums[i + 1]`


**代码:** 



package main

import “fmt”

func findPeakElement(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
}

func main() {
nums := []int{1, 2, 3, 1}
fmt.Println(findPeakElement(nums))
nums = []int{1, 2, 1, 3, 5, 6, 4}
fmt.Println(findPeakElement(nums))
}


#### 有效的完全平方数


Valid Perfect Square


给定一个 **正整数** `num` ,编写一个函数,如果 `num` 是一个完全平方数,则返回 `true` ,否则返回 `false` 。


**进阶:不要** 使用任何内置的库函数,如  `sqrt` 。


**示例 1:**



**输入:**num = 16
**输出:**true


**示例 2:**



**输入:**num = 14
**输出:**false


**提示:**


* `1 <= num <= 2^31 - 1`


**`代码:`**



package main

import “fmt”

func isPerfectSquare(num int) bool {
left, right := 1, num
for left <= right {
mid := left + (right - left) / 2
square := mid * mid
if square == num {
return true
} else if square < num {
left = mid + 1
} else {
right = mid - 1
}
}
return false
}

func main() {
fmt.Println(isPerfectSquare(16))
fmt.Println(isPerfectSquare(14))
}


#### 分割数组的最大值




![img](https://img-blog.csdnimg.cn/img_convert/e327f143af4d6eacb9a14b8a3b6065dc.png)
![img](https://img-blog.csdnimg.cn/img_convert/88733fb24b416a1e4a491382f844ac6c.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

(right - left) / 2
		square := mid * mid
		if square == num {
			return true
		} else if square < num {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	return false
}

func main() {
	fmt.Println(isPerfectSquare(16))
	fmt.Println(isPerfectSquare(14))
}

分割数组的最大值

[外链图片转存中…(img-RuXAs02I-1715721053240)]
[外链图片转存中…(img-Mp5U52Ml-1715721053240)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值