Leetcode 704 二分查找
二分查找
是一种效率较高的查找方法。特点:要求线性表必须采用顺序结构。
主要流程
- 初始状态,设置left、right、middle指针,分别对应线性表的头部、尾部和中间位置。
- 设置循环查找的退出条件:right >= left
- 判断中间位置的值与目标大小的关系
- 若目标值小于中间值,则right = middle - 1
- 若目标值大于中间值,则left = middle + 1
- 若目标值等于中间值,则返回中间指针对应的线性表位置
- 若没有找到,返回-1
根据二分查找的类型,需要对一些细节进行修正
- 左闭右闭型:右指针对应的值参与查找。
- 判断条件:right >= left
- 若目标值小于中间值,则right = middle - 1
- 左闭右开型:右指针对应的值不参与查找。
- 判断条件:right > left
- 若目标值小于中间值,则right = middle
时间复杂度
假设具有 n 个元素,则第一次查找剩余 n/2 个元素,第二次剩余 n/2/2个元素......以此类推
假设查找了 k 次,则剩余个元素。最坏的情况是查找到最后一个元素,则有等式: ,得:,则二分查找的时间复杂度为:
算法实现
初始状态:
查找过程:
结束状态:
搜索区间左闭右闭型:
func search(nums []int, target int) int {
left := 0
right := len(nums) - 1
middle := 0
// 左闭右闭
// 如果是左闭右开,则right > left ; right = middle
for right >= left {
middle = (left + right) / 2
if nums[middle] < target {
left = middle + 1
}
if nums[middle] > target {
right = middle - 1
}
if nums[middle] == target {
return middle
}
}
return -1
}
搜索区间左闭右开型:
func search(nums []int, target int) int {
left := 0
right := len(nums) - 1
middle := 0
// 左闭右闭
// 如果是左闭右开,则right > left ; right = middle
for right > left {
middle = (left + right) / 2
if nums[middle] < target {
left = middle + 1
}
if nums[middle] > target {
right = middle
}
if nums[middle] == target {
return middle
}
}
return -1
}
此处给出了二分查找的进阶使用方式:查找连续存在的目标值的左界或者右界
例子:[1,2,2,2,3],需要查找第一个2出现的位置
相关题目
Leetcode 27 移除元素
暴力解法
使用两层循环,一层循环遍历数组元素,一层循环更新数组元素
时间复杂度:
func removeElement(nums []int, val int) int {
num := 0
size := len(nums)
for i := 0; i < size; i++ {
if nums[i] == val {
num++
for j := i; j < size-1; j++ {
nums[j] = nums[j+1]
nums[j+1] = -1
}
i--
size--
}
}
return size
}
关键点
- 有效数组长度,需要随着元素的删除,相应的减少,否则当测试样例的最后一位为目标值时,程序会在i--处卡住(此处是本人在写暴力算法时遇到的一个bug)
双指针解法
双指针解法本质:只使用一层循环来完成两个循环的工作。在做题时,需要明确快慢指针的含义,方便实现算法
在本题中
- 慢指针:用于指向新数组的下标
- 快指针:用于寻找非目标值,也就是新数组的元素
基于上述含义,我们可知,当快指针遇到目标值时跳过,当遇到非目标值时,将此处值更新至慢指针处,此时慢指针往后移一位
时间复杂度:
func removeElement1(nums []int, val int) int {
slowPoint := 0
for fastPoint := 0; fastPoint < len(nums); fastPoint++ {
if nums[fastPoint] != val {
nums[slowPoint] = nums[fastPoint]
slowPoint++
}
}
//for i := 0; i < slowPoint; i++ {
// fmt.Printf("%v ", nums[i])
//}
return slowPoint
}