二分法代码笔记

二分法代码笔记

最近复习二分法的题目,发现左右区间的二分写法总是无法第一时间写出正确的,故痛定思痛,通过写笔记的形式记录下来。

这里需要说明的是,二分法多用于单调情况下的LogN复杂度的搜索,并非只用于排序数组。二分法是一种高效的搜索方法,前提是能分析出问题是随某一个自变量的单调函数

参考 labuladong - 二分查找

PS: 十分感谢 labuladong的微信文章,学习算法分门别类很重要!

0 入参

int[] nums, int target

1 找一个数

  • 搜索区间
    • 根据 left 和 right 的定义,可知搜索区间为 [left, right]
  • 停止条件为
    • 找到了: return mid;
    • 没找到: when left = right + 1, return -1;
  • 左右子搜索区间分别为(此时 nums[mid] != target)
    • 左 [left, mid - 1]
    • 右 [mid + 1, right]
int left = 0;
int right = nums.length - 1;
while (left <= right) {
    int mid = left + (right - left) / 2;
    int midVal = nums[mid];
    if (midVal == target) {
        return mid;
    } else if (midVal < target) {
        right = mid - 1;
    } else {
        left = mid + 1;
    }
}
return -1;

2 找左侧边界

小提醒,查找左右边界,都是要检查最后查找的元素 nums[i] == target

如果不等于,则数组中没有 target 元素

  • 定义
    • 返回有序数组中,小于 target 值的元素的个数
    • 或者,第一个等于target 的下标
    • 返回值 i 的取值, nums[i] >= target
  • 返回值的取值范围是 [0,nums.length]
    • 讨论越界下标的情况 nums.length
    • 当 left = right = nums.length时
    • 不会进入while,直接返回 left = right = nums.length
    • 所以不存在越界问题
  • 核心思想
    • 搜索区间[left, right)向左收缩
    • 检测完 nums[mid]之后,应当去掉mid,将区间划分为:
      • [left, mid)
      • [mid + 1, right)
  • 搜索区间
    • 根据 left 和 right 的定义,可知搜索区间为 [left, right)
  • 终止条件
    • left = right ,此时返回 left 和 right 都可以
int left = 0;
int right = nums.length;
while (left < right) {
    int mid = left +  (riht - left) / 2;
    int midVal = nums[mid];
    if (midVal == target) {
        right = mid;
    } else if (midVal < target) {
        left = mid + 1;
    } else {
        right = mid;
    }
}
return left;

3 找右侧边界

与左侧边界相似

  • 定义
    • 返回有序数组中,<= target 值的元素的下标
    • 或者,最后一个等于target 的下标
    • 返回值 i 的取值, nums[i] <= target
  • 返回值的取值范围是 [0,nums.length]
    • 讨论越界下标的情况 nums.length
    • 当left = right = nums.length 时
    • 不会进入while,直接返回 left = right = nums.length
    • 所以不存在越界问题
  • 核心思想
    • 搜索区间[left, right)向右收缩
    • 检测完 nums[mid]之后,应当去掉mid,将区间划分为:
      • [left, mid)
      • [mid + 1, right)
int left = 0;
int right = nums.length;
while (left < right) {
    int mid = left + (right - left) / 2;
    int midVal = nums[mid];
    if (midVal == target) {
        left = mid + 1;
    } else if (midVal < target) {
        left = mid + 1;
    } else {
        right = mid;
    }
}
return left - 1;

单独讨论 return left - 1 这条语句,由于最终的最终,left = right = mid + 1;

此时,可知nums[mid] <= target

由于 left = right = mid + 1

可以推测 nums[left] >= target

存在大于的风险,因此返回 mid 就行了,

由于可能存在不进入循环,mid是while中的局部变量

因此返回 left - 1 或者 right - 1 就行了,越界章节开头已经讨论过。

posted @ 2022-08-18 18:53  jentreywang  阅读(65)  评论(0编辑  收藏  举报