二分查找入门到放弃

本文探讨了二分查找算法的两种常见实现方式——左闭右闭和左闭右开。作者强调了理解这两种方法的重要性,并指出代码的易读性比精简更重要。在实践中,左闭右闭的写法更符合作者的思维方式,而左闭右开则常用于简化循环结束条件。文章还提供了处理特殊情况(如重复元素、不存在答案)的示例代码,强调了在不同场景下选择合适方法的灵活性。

二分查找

  二分查找的代码,总是一看就懂,一写就费。究其原因,二分难就难在其复杂的边界处理上。看了一些博客和总结之后,可以将二分的写法总结为两种方法,根据搜索区间分为两类:左闭右开和左闭右闭

  这里谈谈我自己在学习过程中对这两种写法的理解。首先,这两种思路并不是矛盾的,对于一个查找问题,使用两个中的任何一个都可以。其次,我认为代码在于易读,而不在于精简,在理解了某种思路的基础上一点点对代码精简是没有问题的,但是不能一上来就是王炸级别的几行代码,完全丧失了代码的易读性。最后,我更习惯使用左闭右闭的写法,更符合自己的思维方式。

1. 左闭右闭

  不考虑bug-free的一般情况下,查找排序数组中的target的模板如下。

func getFirst(nums []int, k int) int {
	left, right := 0, len(nums) - 1
	for left <= right {
		mid := left + (right - left) / 2
		if nums[mid] == k {
			return mid
		} else if nums[mid] < k {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	return -1
}
  1. 初始条件,right := len(nums) - 1
  2. 循环不变量,left <= right
  3. left和right的 更新逻辑
		if nums[mid] == k {
			return mid
		} else if nums[mid] < k {
			left = mid + 1
		} else {
			right = mid - 1
		}

  二分查找的难点就在于上面的三个细节的确定。因为经常看到初始条件为right = len(nums), 循环不变量为left < right,更新right时right = mid等情况。其实,完全没必要将这些点混淆,理解模板中左闭右闭的写法后,再针对一些特殊情况(例如有重复元素,答案不存在等)进行一些处理。

   事实上,输入的数组并不永远都是完美的,可能出现如 有重复元素,答案不存在,区间为空,搜索上下界 等特殊情况。

// 查找最左侧
func getFirst(nums []int, k int) int {
	left, right := 0, len(nums) - 1
	for left <= right {
		mid := left + (right - left) / 2
		if nums[mid] == k {
			if mid == 0 || nums[mid-1] != k {
				return mid
			} else {
				right = mid - 1
			}
		} else if nums[mid] < k {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	return -1
}

// 查找最右侧
func getLast(nums []int, k int) int {
	left, right := 0, len(nums) - 1
	for left <= right {
		mid := left + (right - left) / 2
		if nums[mid] == k {
			if mid == len(nums) - 1 || nums[mid+1] != k {
				return mid
			} else {
				left = mid + 1
			}
		} else if nums[mid] < k {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	return -1
}

  很多技术博客中,都建议使用左闭右开 [left, right) 的情况。原因是循环结束时left==right,这样就不用关心是返回left还是返回right了。实际上,只要理解了最终的代码中的过程,通过[left, right]的方式根据需求决定返回left还是right会更加灵活。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值