- 二分的本质是两段性,并非单调性
- 什么时候可以运用二分法:只要一段都满足某中性质,另外一段都不满足某中性质,就可以使用二分法
- 二分有序查找只是二分法的最常见的应用,前面一段都小于target(查找数),后面一段都不小于target,于是可以使用二分
- 更常规题目比如
- 0000000111111 找到第一个1 前半段都等于0,后半段都不等于0 所以可以使用二分
- 789123(旋转数组问题,寻找突变点) 前半段(789)都>=7(第一个元素),后半段(123)都<7,所以可以使用二分
- 13542(峰顶问题,寻找峰顶元素)前半段(135)当前元素都比它前面一个大,后半元素(42)当前元素都比它前面一个小,所以可以使用二分(或者这样理解:前边段数据处于上升阶段,后半段元素处于下降阶段,这个就是某种性质,那么怎么用代码表示这种性质呢,就是前面说的当前元素都比它前面一个大(上升=> arr[i]>arr[i-1]),当前元素都比它前面一个小(下降 => arr[i]<arr[i-1]))
- 所以关键是找出每个题目属于自己的某种性质,让中间指针去和这种性质做比较,看哪段符合(保留,继续二分),哪段不符合(直接舍弃)
/*
[剑指 Offer II 069. 山峰数组的顶部](https://leetcode.cn/problems/B1IidL/)
思路 前半段 当前元素都比它前面一个大(性质),后半元素 当前元素都比它前面一个小
*/
var peakIndexInMountainArray = function (arr) {
let l = 0;
let r = arr.length - 1;
while (l <= r) {
let m = l + ((r - l) >> 1);
if(m===0) return 1 //防止下面arr[m-1]越界,去掉只有少数案例通不过,最核心的还是下面某中性质
if (arr[m] > arr[m - 1]) l = m + 1;//arr[m] > arr[m - 1]就是要找的某种性质
else r = m - 1;
}
return r; //至于返回l r自己判断
};
例子2
const arr = [false,false, false,false,true,true,true, true,true,true,true];
/**
* 如何找到第一个为true的索引
* 思路:0000000111111 找到第一个1 前半段都等于0(性质),后半段都不等于0 所以可以使用二分
*/
function findFisrtTrue(arr) {
console.log(arr);
let l = 0;
let r = arr.length - 1;
while (l <= r) {
let m = l + ((r - l) >> 1);
if (arr[m] === false) l = m + 1; //arr[m]===false就是某种性质
else r = m - 1;
}
return l; //至于返回l r自己判断
}