704.力扣LeetCode_二分查找

二分法(折半查找)
int search(int* nums, int numsSize, int target) {int left=0,right=numsSize-1,mid;while(left<=right){mid=left+(right-left)/2;if(nums[mid]==target) return mid;if(nums[mid]>target){right=mid-1;}else{left=mid+1;}}return -1;
}
二分查找的判定树
在二分查找中,我们通过不断地将搜索区间对半分,来决定目标值的位置。在每一步,我们通过比较当前区间的中间元素与目标值来决定接下来的搜索方向。这个过程中可以形成一棵二叉树,其中每个节点表示一次比较,每个叶子节点则代表查找的结果。我们可以将这棵树称为二分查找的判定树。
二分查找的分割方式
在二分查找过程中,每次我们都将搜索区间分为两部分。假设当前的区间是 [left, right],那么每次我们选择区间的中间元素 mid,然后比较目标值与 mid:
如果目标值小于 mid,则下一步将在区间 [left, mid-1] 中继续查找;
如果目标值大于 mid,则下一步将在区间 [mid+1, right] 中继续查找;
如果目标值等于 mid,则查找成功,直接返回。
每一次比较都使得搜索区间的大小减少一半。这样,二分查找的判定树也就由这些不断对半分割的操作构成。
判定树的平衡性
二分查找的递归过程意味着在树的每一层,都会有两个分支:一个对应左半部分,另一个对应右半部分。每个节点都基于对当前区间的中间元素进行比较所生成的两个子节点。理想情况下,每次分割区间时,左子树和右子树的大小差异非常小,几乎相等,这就使得二分查找的判定树保持平衡。
由于每次分割区间都尽可能地将搜索空间对半分,这就保证了二分查找的判定树不会出现一侧远大于另一侧的情况。如果某一子树比另一子树大得多,就意味着在该区域内的查找不再是二分的,而是偏向于单侧查找。因此,二分查找的判定树在任何时刻,左右子树的大小差异不会超过 1,这意味着树的结构是平衡的。
在一个包含n个元素的有序数组中,二分查找的树的高度最多为logn。每一次分割都将当前区间减半,因此查找的深度是对数级别的。由于二分查找每次的分割是均匀的,树的高度保持平衡,从根节点到叶子节点的路径长度相对一致,不会出现一侧的路径远长于另一侧。
二分查找的适用场景
查找有序数组中的元素
二分查找最常见的应用场景是查找有序数组中的元素。如果给定的数组是升序或降序排列的,二分查找可以在对数时间内迅速定位目标元素。相比线性查找(时间复杂度为 O(n)),二分查找大大提高了效率,尤其在数据量较大的情况下效果尤为明显。例如,在大规模数据库查询中,经常会使用二分查找来定位某个特定的记录。
插入位置查找
除了查找元素外,二分查找还可以用于查找元素应该插入的位置,尤其是在插入有序数组时。这种应用通常称为“二分插入”问题。例如,给定一个有序数组,如何快速找到一个新元素应该插入的位置,保持数组的有序性?二分查找可以帮助我们在 O(log n) 的时间内找到正确的位置。这在动态数据结构中非常有用,比如维护一个动态排序的集合时。
数值计算和求解问题
二分查找也经常用于一些数值计算的问题。例如,求解一个方程的近似解或查找函数值的某个极限。在这类问题中,我们往往没有明确的解析解,而是需要通过二分查找在某个区间内逼近目标值。一个典型的例子是求解“平方根”的问题。例如,给定一个数 x,我们可以通过二分查找来快速逼近其平方根。
与动态规划结合的查找
在一些复杂的算法中,二分查找与动态规划相结合,用于提高效率。例如,在一些求最短路径、最小生成树或最长公共子序列的问题中,二分查找经常被用来加速特定状态下的查找过程。通过二分查找,我们可以快速缩小搜索范围,从而减少不必要的计算。
查找模式匹配中的边界
在一些字符串匹配算法中,二分查找用于解决模式匹配中的边界问题,例如,查找给定模式首次出现的位置。通过对匹配结果进行二分查找,可以在已排序的匹配结果中迅速定位到某个特定位置,这在文本处理和搜索引擎的实现中非常常见。
