今天做一道题发现二分,但是关于循环的时候什么时候用 left < right ,什么时候用 <=
什么时候用 left = mid +1 还是left = mid 又搞不清了了
几天借着这些问题、在来梳理一下,其实这些选择都跟边界选择有关
704. 二分查找
左闭右闭 []
left,right := 0, len(arr) - 1 // 因为是左闭右闭区间
for left <= right {
// 为什么,因为左闭右闭,左边界和右边界可以相等
if arr[mid] > right{ // 更新右边界
right = mid -1 // 此时mid 这个值是无用的,不被包含在区间中,所以可以要用mid - 1
}else if arr[mid] < right{ 更新左边界
left = mid + 1 // 同理,mid这个值在需要的区间之外,大家可以画个图更形象
}else{
return mid
}
}
左闭右开区间[)
for left < right {
// 为什么,因为左闭右开,右边界不被包含在区间中,左边界和右边界不可以相等
if arr[mid] > right{ // 更新右边界
right = mid // 此时mid 这个值是无用的,但是右边界是开区间,所以可以等于mid
}else if arr[mid] < right{ // 更新左边界
left = mid + 1 // 同理,mid这个值在需要的区间之外,大家可以画个图更形象
}else{
return mid
}
}
有重复元素的二分,查找第一个元素位置和最后一个元素位置
思路
使用左闭右闭区间,那么left 可以等于 right
接下来就是关于mid的边界判定
if num[mid] > target r = mid - 1// 收缩右边界
if num[mid] < target l = mid + 1// 收缩左边界
if num[mid] = target r = mid - 1// 如果要找第一个重复元素这时候就要继续收缩右边界,知道区间所有元素都小于target,那时候最后一步判断l会向右移位一步找到target
Code
func searchRange(nums []int, target int) []int {
return []int{findLeftNum(nums, target), findRightNum(nums, target)}
}
func findRightNum(nums []int, target int) int {
l, r := 0, len(nums)-1
for l <= r {
mid := l + (r-l)/2
if nums[mid] < target {
l = mid + 1
} else if nums[mid] > target {
r = mid - 1
} else {
l = mid + 1
}
}
// 边界判定,这时候要输出r,而r一直在减小
if r < 0 || nums[r] != target{
return -1
}
return r
}
func findLeftNum(nums []int, target int) int {
l, r := 0, len(nums)-1
for l <= r {
mid := l + (r-l)/2
if nums[mid] < target {
l = mid + 1
} else if nums[mid] > target {
r = mid - 1
} else {
r = mid - 1
}
}
if l >= len(nums) || nums[l] != target{
return -1
}
return l
}
33. 搜索旋转排序数组
[TOC]
思路1
使用二分,把一个完成的区间分成两块,一块有序,一块无序
有序无序可以通过 num[0] 和 nums[mid]的关系判断, nuns[0] <= nums[mid],说 明 0,mid 是有序的,可以通过二分判断这个区间
反之,就判断 mid,r 区间的数据
Code
func search(nums []int, target int) int {
// 根据第一个节点和mid的关系判断是否有序
l, r := 0, len(nums)-1
for l <= r {
mid := l + (r-l)>>1
if nums[mid] == target {
return mid
}
if nums[0] <= nums[mid] {
if nums[mid] > target && nums[l] <= target {
r = mid - 1
} else {
l = mid + 1
}
} else {
if nums[mid] < target && nums[r] >= target {
l = mid + 1
} else {
r = mid - 1
}
}
}
return -1
}
- 思路二:就是把数组变成有序
这种方法其实有些取巧,如果有数字x< nums[0],就把这个数字 +10000 (观察题发现数字最大是10000),这样就能直接用二分来搜索了
func search(nums []int, target int) int {
// 根据第一个节点和mid的关系判断是否有序
target += 10000
l, r := 0, len(nums)-1
for l <= r {
mid := l + (r-l)>>1
if nums[mid] < nums[0] {
nums[mid] += 10000
}
if nums[mid] == target {
return mid
}
if nums[mid] > target {
r = mid - 1
} else {
l = mid + 1
}
}
return -1
}