原理
顾名思义,在指定的有序序列中,通过每次对半分的方式拆开检索(通过条件判断排除不符合条件的其中一半,而对另一半继续拆分检索),直到找到想要找的元素;效率上会比单次循环来的快。
二分查找的时间复杂度
关于二分查找的时间复杂度 O(log2N),可以看这篇文章:
二分查找基本套路
首先需要明确:二分查找是针对有序数列
-
确定查找的区间:在有序序列的起始位置 start 和 结束位置 end 中间查找
-
确定 中间位置的算法(注:取mid中间值方法不固定,也可采用位运算):Math.floor((start + end)/2)
-
确定循环条件:查找范围在
[start, end]左右闭区间中, 当start <= end时,循环将继续进行下去 -
整个执行流程如下(以升序为例):
1>. 指定 start 和 end 初始值 分别为 有序数列的开始位置和结束位置
2>. 执行一次循环,循环中获取到当前 start 和 end 中间值 mid
* 如果 `target === mid`, 则 直接返回 mid * 如果 `target > mid`, 则 `start = mid + 1` * 如果 `target < mid`,则 `end = mid -1` * 以上为基本套路,不同题型需要根据其要求进行变化;但整体思路就是这样的3>. 在循环结束后仍未找到的,返回一个默认值即可
二分查找-测试题
33. 搜索旋转排序数组
题目:整数数组 nums 按升序排列,数组中的值 互不相同 。在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
解题方法:
- 单次循环:遍历数组中所有元素直接找到目标元素结束
var search = function(nums, target) {
let index = -1;
for(let i = 0; i < nums.length; i+=1){
if (nums[i] === target) {
index = 1;
break;
}
}
return index;
};
- 双指针:从开头和结尾同时开始查找,直到找到目标元素;比单次循环快1倍
var search = function(nums, target) {
let index = -1;
for (let i = 0, j = nums.length -1; i <= j;i++,j--) {
if (nums[i] === target) {
index = i
break;
}
if (nums[j] === target) {
index = j;
break;
}
}
return index;
};
-
二分查找
解题思路: 虽然在某个节点旋转,但采用二分法必定会得到一个有序一个无序的数组,可以通过判断目标元素是否在有序的那一边
- 首先判断 nums[mid] 与 最左侧 数组 第一个值进行比较
- nums[mid] >= nums[left],则左侧有序
- nums[mid] < nums[left], 则右侧有序
- 再通过判断,目标值是否在有序数组中,对 left 和 right 区间做加减处理,继续二分
- 首先判断 nums[mid] 与 最左侧 数组 第一个值进行比较
var search = function(nums, target) {
if (nums.length <= 0) return -1;
let mid, left = 0, right = nums.length -1;
while(left <= right) {
mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid
}
if (nums[mid] >= nums[left]) {
if (target >= nums[left] && target <= nums[mid] ) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (target >= nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
};
其他相关测试题
34. 在排序数组中查找元素的第一个和最后一个位置
74. 搜索二维矩阵
总结
- 了解了二分查找的原理,二分查找时间复杂度log2N 的计算过程
- 知道了二分找找的局限(有序/数组或链表)
- 二分查找关键点在于,找到取舍左右半边的判断条件