题意
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4
这个题啊,有一说一,不知不觉做了三遍,这三遍的解法都不一样
第一次解,小白做法,直接for循环暴力查找
/**
* @param {number[]} nums
* @param {number} target
* @return {number} */
var search = function (nums, target) {
if (nums.indexOf(target) !== -1) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] === target)
return i
}
}
return -1
};
第二次,用的二分查找
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
var upperBound=nums.length-1;
var lowerBound=0;
while(lowerBound<=upperBound){
var mid = Math.floor((upperBound+lowerBound)/2);
if(nums[mid]<target){
lowerBound = mid + 1;
}else if(nums[mid]>target){
upperBound = mid - 1;
}else{
return mid
}
}
return -1
};
第三次,就一行代码(对数组一些API用得比较熟悉了)
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
return nums.findIndex(item=>item===target);
};
但是我们可以看到,这三遍下来时间复杂度最低的还是二分查找
所以这边重点来分析一下 二分查找
概念
啥叫二分查找?
简单来说,就是一分为二
一个图给你整明白了。哦不,这是我自己的图,给我整明白了。
你们不明白,就去看这个
拿mid和target做对比,不断缩小区间的同时,mid也是在改变的,直到mid===target
前置条件
满足用二分查找的前提条件:
-
有序数组
-
无重复元素
解释下上面俩前置条件是为啥:
有序数组是因为,当我们用二分查找需要用到一个判断条件叫left<=right
没有重复元素其实也不算特别必要的条件,只是说,如果存在重复元素的话,最终可能会出现多个满足target的index,这样的话,题目就是另外一回事儿了
思想
主要目的是掌握 二分查找 的思想
我们这个二分查找,是基于数组的基础去理解的,数组我们知道,它的内存空间是连续的,只要我们想要查找一个数组中的元素,那我们每一次查找都需要遍历一遍数组,它的 时间复杂度是O(n)
那么二分呢,二分查找肯定是基于数组的对吧
二分相当于遍历的那个次数折半了,所以它的时间复杂度是O(log2n)
它比起普通的for循环遍历,效率要高不少,不过第三种解法,代码真的少很多啊!!!
以后如果侧重考虑性能问题,还是写二分查找,不然我其实emm还是更喜欢第三种哈哈哈