跳到主要内容

Go 练习题里最值得反复写的一类:递归和二分查找

· 阅读需 4 分钟
一介布衣
全栈开发者 / 技术写作者

很多人刷算法题时喜欢挑复杂题,觉得这样更有挑战。但从练代码基本功的角度看,真正值得反复写的,往往是那些规则简单、边界丰富的小题。递归和二分查找就是这种题型的代表。它们不靠花哨技巧取胜,却非常适合训练你是不是能把停止条件、输入约束和函数职责写清楚。

二分查找看着短,最容易暴露边界漏洞

二分查找的核心逻辑谁都知道:取中间、比较大小、缩小区间。可真正落到代码里时,经常会出现死循环、漏掉末尾元素、下标越界等问题。原因并不在算法难,而在于你是否真的把区间定义想明白了。

我后来练这类题时,会先在纸上写清楚当前区间到底是闭区间还是半开区间,再动手写代码。因为一旦这个前提没统一,后面的 left <= rightmid + 1right = mid - 1 都很容易互相打架。

递归最适合训练“何时结束”这件事

递归题的重点从来不是“会不会调用自己”,而是你能不能明确说出什么时候该停。很多初学者写递归时,只顾着把问题继续往下拆,却忘了终止条件才是整个函数最重要的那一行。

像阶乘、斐波那契、最大公约数这些经典题,本质上都在训练同一件事:当问题缩小到什么程度时,可以直接给出答案;当还没到那个程度时,下一次递归参数应该怎么变化。这个过程如果能想清楚,很多复杂递归题也不会太慌。

Go 写算法时,函数签名要先稳住

我越来越觉得,算法练习和日常业务编码并不是完全割裂的。比如二分查找函数该返回索引、布尔值还是错误信息,递归函数该接收整个切片还是左右边界,这些设计都很像业务代码里的接口边界问题。

如果你一开始就把函数签名定得很随意,后面边界一多就会不断改参数,代码越来越乱。反过来,如果先把输入和输出定义清楚,真正实现逻辑时反而会更稳。

func binarySearch(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
}
if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}

这段代码本身不复杂,但它很适合拿来反复练习“搜索不到时返回什么”“空切片怎么处理”“有重复元素时语义是什么”这些细节。

练习时不要只满足于“样例通过”

算法题最容易形成坏习惯的地方,就是样例一过就觉得结束了。实际上真正有价值的训练是主动补边界:空数组、单元素、目标值在头尾、不存在目标值、重复值、极小区间。你补得越多,代码的可靠性就越像真实项目,而不是只为过题。

这也是我为什么一直觉得基础题值得反复写。因为它们能逼着你面对这些细节,而不是靠题解模板掩盖问题。

递归和二分查找,练的是算法,也是代码习惯

把这两类题写顺了之后,你会发现受益的不只是刷题本身。你在业务代码里写查找逻辑、拆小函数、定义终止条件、处理边界输入时,都会更有感觉。很多时候我们缺的并不是“更难的题”,而是把简单题真正写稳的耐心。