引言
大家好啊,我是前端拿破轮😁。
跟着卡哥学算法有一段时间了,通过代码随想录的学习,受益匪浅,首先先卡哥致敬🫡。
但是在学习过程中我也发现了一些问题,很多当时理解了并且AC的题目过一段时间就又忘记了,或者不能完美的写出来。根据费曼学习法,光有输入的知识掌握的是不够牢靠的,所以我决定按照代码随想录的顺序,输出自己的刷题总结和思考。同时,由于以前学习过程使用的是JavaScript,而在2025年的今天,TypeScript几乎成了必备项,所以本专题内容也将使用TypeScript,来巩固自己的TypeScript语言能力。
题目信息
二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
题目分析
本题是基础的二分查找,但是想要完全AC却不是很容易。很多同学是这次AC了下次就不一定,或者就算AC了也没有梳理清楚思路,没有AC的时候也不知道原因。
之所以会出现这些情况,主要是因为对于二分查找区间的开闭性没有定义清楚。比如最开始的时候到底right是应该等于nums.length呢还是nums.length-1呢?如果我们去看网上的题解,会发现两种情况都有,而且都能AC。同样的还有,当nums[mid] > target时,right是应该等于mid呢还是mid-1呢?
这两个问题本质上都是同意个原因导致的,就是区间的开闭性定义。
关于区间,我们高中数学就学过。
- 小括号( )表示开区间,比如(1,3),就表示1到3之间的所有数,但是不包括边界1和边界3。
- 中括号[ ]表示闭区间,比如[1,3],就表示1到3之间的所有数,但是包括边界1和边界3。
所以我们在处理二分问题时,一定要明确我们的区间定义,并且整个题解中保持一致。
常见的题解通常是两种方式,一种是左闭右开,就是区间定义包括左边界,不包括右边界;另一种就是闭区间,同时包括左右边界。当然也可以有其他的任意区间定义,只需要在解体过程中保证一致即可。
题解
左闭右开法
在左闭右开的区间定义中,不包括右边界,所以初始的right值一定是nums.length,而不是nums.length - 1。试想,如果right的值是nums.length - 1,而我们是左闭右开,意味着搜素区间中不包括最后一个元素,很显然这是不合理的,因为我们的target有可能就是最后一个元素,这样的话,我们就永远无法搜索到。
function search(nums: number[], target: number): number {
// 定义左右指针
let left: number = 0;
let right: number = nums.length;
// 由于是左闭右开,所以循环条件是left < right,不能等于
// 因为没有区间[3,3),不可能有一个数既大于等于3,又小于3
while (left < right) {
// 计算中间值: 利用位运算加快运算速度,通过右移差值防止大数溢出
let mid = left + ((right - left) >> 1);
// 判断中间值和目标值的大小
if (target < nums[mid]) {
// 如果目标值小于中间值,说明目标值可能在左区间,将右指针移动到中间位置
// 因为nums[mid]肯定不是目标值,而我们的区间【不包括】right,所以让right=mid即可
right = mid;
} else if (target > nums[mid]) {
// 如果目标值大于中间值,说明目标值可能在右区间,将左指针移动到中间位置
// 因为nums[mid]肯定不是目标值,而我们的区间【包括】left,所以让left=mid+1即可
left = mid + 1;
} else {
// 如果中间值等于目标值,返回中间位置
return mid;
}
}
// 如果最终没有找到目标值,返回-1
return -1;
};
闭区间(左闭右闭)法
通过上面对左闭右开的解释,相信大家已经理解了意思。所以左闭右闭不再详细解释。
function search(nums: number[], target: number): number {
// 定义左右指针
let left: number = 0;
let right: number = nums.length - 1;
// 循环
while (left <= right) {
// 计算中间值
let mid = left + ((right - left) >> 1);
if (target < nums[mid]) {
right = mid - 1;
} else if (target > nums[mid]) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
};
复杂度分析
时间复杂度:
空间复杂度;
二分法折半查找,空间复杂度没什么好说的。
整个过程只有指针和临时栈变量,只用常数空间,故空间复杂度为。
总结
本文讨论了leetcode704的二分查找题目,分析了解体过程中常见的困惑点,关键在与对于区间开闭的定义。给出了左闭右开和闭区间两种方法的TypeScript的AC代码,分析了算法的时间和空间复杂度。
好了,这篇文章就到这里啦,如果对您有所帮助,欢迎点赞,收藏,分享👍👍👍。您的认可是我更新的最大动力。
往期推荐✨✨✨
我是前端拿破轮,我们下期见!