简单LeetCode 704可逐步演示引导学习
二分查找
在升序数组中查找目标值
每一步都和搜索区间的中点比较,一次砍掉一半区间,把 O(n) 的查找变成 O(log n)。
时间复杂度 O(log n)空间复杂度 O(1)
所属套路:二分查找框架
题目描述
给定一个升序排列的整数数组 nums 和一个目标值 target,在数组中查找 target,返回它的下标;如果不存在,返回 -1。要求时间复杂度为 O(log n)。
学习目标
能说清二分为什么是"每次排除一半"、循环条件与收缩方式为什么必须配套,并能独立处理边界。
已完成 0 / 6 阶段
诊断
先看条件:有序带来了什么
数组有序,要找一个数。线性扫描和二分查找,差别到底在哪?
建议先完成当前阶段(提示 / 检查 / 自评),再进入下一步;也可以直接跳过。
解题思路
核心思想
每一步都和搜索区间的中点比较,一次砍掉一半区间,把 O(n) 的查找变成 O(log n)。
为什么需要它
数组是有序的——这个条件不用就浪费了。有序意味着任意两个元素的大小关系都是已知的,所以比较一次就能确定目标在左半还是右半,根本不用逐个检查。
第一反应:直觉方案
最直接的办法是线性查找:从头到尾挨个看,最坏要看 n 个元素。能工作,但完全没利用"有序"这个已知信息。
关键洞察
关键不是"猜得准",而是"每次排除得多"。找到中点后,无论比较结果是哪种,总能安全地排除掉一半区间——包括中点自己在内。n → n/2 → n/4 → … → 1,最多只需要 log₂n 次比较。这就是"更聪明地排除"胜过"更努力地查找"。
时间复杂度 O(log n)空间复杂度 O(1)
易错点
- 循环条件写 lo <= hi(写成 lo < hi 会漏掉区间只剩一个元素的情况)
- 区间收缩必须用 mid + 1 / mid - 1,写成 mid 会死循环
- 取整方向要和收缩方式配套:JS 的 Math.floor 向下取整、mid 偏左;配合 mid + 1 / mid - 1 才不会卡住
逐步演示
参考代码
1function search(nums, target) {
2 let lo = 0, hi = nums.length - 1;
3 while (lo <= hi) {
4 const mid = Math.floor((lo + hi) / 2);
5 if (nums[mid] === target) {
6 return mid;
7 } else if (nums[mid] < target) {
8 lo = mid + 1;
9 } else {
10 hi = mid - 1;
11 }
12 }
13 return -1;
14}
效果图
预测:答对 0 / 共 1 题
第 1 / 10 步