二分法

221 阅读1分钟
  • 二分的本质是两段性,并非单调性
  • 什么时候可以运用二分法:只要一段都满足某中性质,另外一段都不满足某中性质,就可以使用二分法
  • 二分有序查找只是二分法的最常见的应用,前面一段都小于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,falsefalse,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自己判断
}