二分查找
一、什么是二分法。
定义:对于区间 [a,b] 上连续不断且 f(a)· f(b)<0的函数 y = f(x),通过不断地把函数 f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法(也称为二分查找或折半查找)。(百度)
算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。
基本思想:
假设数据是按升序排序的,对于给定值 key,从序列的中间位置 k 开始比较, 如果当前位置 arr[k] 值等于 key,则查找成功;
若 key 小于当前位置值 arr[k],则在数列的前半段中查找, arr[low, mid-1];
若 key 大于当前位置值 arr[k],则在数列的后半段中继续查找 arr[mid+1, high], 直到找到为止,时间复杂度: O(log(n))
二、常用来解决什么问题
- 查找特定元素:在有序数组中查找特定元素的索引位置。通过反复将搜索范围缩小为一半,可以在较短的时间内快速找到目标元素或者确定其不存在。
- 判断元素是否存在:在有序数组中确定某个元素是否存在。如果二分查找结束时未找到目标元素,则可以确定目标元素不存在于数组中。
- 查找边界:找到有序数组中满足某种条件的边界,例如最小的大于等于某个值的元素的索引,或者最大的小于等于某个值的元素的索引。
- 求解单调函数的最值问题:对于某些特定类型的函数,例如单调递增或单调递减函数,可以利用二分法求解函数的最大值或最小值。
- 分治算法中的子问题的解决:在一些分治算法中,二分法常常用于解决子问题,例如快速排序中的分区操作就是利用了二分法。
总的来说,二分法适用于有序数组,并且其时间复杂度为 O(log n) ,因此对于大型数据集,二分法通常比线性搜索更加高效。
常见解法,难点
1、常见解法
例如:搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
解法:三种区间方式解答。
-
闭区间
// nums[i] 属于 [low, high] const searchInsert = (nums, target) => { let low = 0, high = nums.length - 1; while (low <= high) { const mid = ((high - low) >> 1) + low; if (nums[mid] >= target) { high = mid - 1; // 范围缩小到 [low, mid-1] } else { low = mid + 1; // 范围缩小到 [mid+1, high] } } return low; // 或者 high+1 } -
左闭右开区间
// nums[i] 属于 [low, high) const searchInsert = (nums, target) => { let low = 0, high = nums.length - 1; while (low < high) { const mid = ((high - low) >> 1) + low; // 新区间需要包括target // 区间 [low, target, mid, right] if (nums[mid] >= target) { high = mid; // 范围缩小到 [low, mid) } else { // 区间 [low, mid, target right] low = mid + 1; // 范围缩小到 [mid+1, high) } } return low; // 或者 high } -
开区间
// nums[i] 属于 (low, high) const searchInsert = (nums, target) => { let low = -1, high = nums.length - 1; while (low + 1 < high) { const mid = ((high - low) >> 1) + low; if (nums[mid] >= target) { high = mid; // 范围缩小到 (low, mid) } else { low = mid; // 范围缩小到 (mid, high) } } return low + 1; // 或者 high }
2、难点
判断单调区间,然后区间得开闭情况。在单调区间内,查找符合条件的数据。
二分法划分的新区间需要包括target,目标条件值 。
常见的二分算法问题
leetcode 题目:
... ...
总结整理,完成。有指正欢迎友好交流,谢谢!