当前位置:   article > 正文

二分查找法---------五大常用算法_二分搜索的算法设计方法有哪些

二分搜索的算法设计方法有哪些

流程:

大佬解析:

核心思想:

图片说明:

 优缺点:

使用条件:

代码:

递归法(不太好):

非递归法:

时间复杂度

 空间复杂度:

进阶(面向更广):

地址:

二分模板一共有两个,分别适用于不同情况。

版本一:

模板:

版本二:

 模板:

例题:

Leetcode034描述:

 自己写的题解:

Leetcode069(找平方根):

题解地址:

Leetcode074(搜索二维矩阵):

Leetcode033、153(搜索旋转排序数组)----------二分法进阶

描述:

 题解地址:


流程:

在经过几道例题的思考以后,我自己针对二分法记下我认为比较合适的解题流程:

1.首先我们要确定左右边界的范围

例如在寻找非负整数x的平方根时,int left=0;int right=x;(主要为了防止x==0或x==1)

2.寻找要找的数与给出的函数的参数之间的关系:

注意这里我们一定要做到呈现一种对立的性质

 以前的我由于一直做的二分法都是寻找数组中等于目标target的数,所以认为函数的参数就是我们要找的数,其实这两者有很大的区别,就比如在寻找平方根这题中:

这里函数里面的参数x,不一定是我们要找的数,它有点像一个参照物,我们要找到性质都要往x靠

 3.接下来就套用两个模板就行了。

大佬解析:

java实现二分查找-两种方式_maoyuanming0806的博客-CSDN博客_二分查找java

核心思想:

有序的序列,每次都是以序列的中间位置的数来与待查找的关键字进行比较,每次缩小一半的查找范围,直到匹配成功。
一个情景:将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

图片说明:

 优缺点:

优点是比较次数少,查找速度快,平均性能好;

其缺点是要求待查表为有序表,且插入删除困难。

因此,折半查找方法适用于不经常变动而查找频繁的有序列表。

使用条件:

查找序列是顺序结构,有序。

代码:

注意这里在评论区指出来使用int middle=(left+right)/2 ; 存在整数溢出的风险,应改为 int middle=left+(right-left)/2 ; 因为int是4字节,有一定范围。如果两个其中一个很大,相加容易超出这个范围,改成相减的形式可以一定程度避免。这种写法值得推崇!

递归法(不太好):

  1. //两种实现方式,第一种使用递归法
  2. public static int search(int[] nums, int key, int left, int right){
  3. if(nums[left]>key||nums[right]<key||left>right) {
  4. return -1;
  5. }
  6. int middle=left+(right-left)/2
  7. //说明要查找的在数组左边
  8. if(key<nums[middle]){
  9. return search(nums,key,left,middle-1);
  10. }
  11. else if(key>nums[middle]){
  12. return search(nums,key,middle+1,right);
  13. }
  14. else {
  15. return middle;
  16. }
  17. }

非递归法:

  1. //第二种使用非递归法
  2. public static int fei(int []nums,int key) {
  3. int left = 0;
  4. int right = nums.length - 1;
  5. int middle=left+(right-left)/2;
  6. while (left <= right) {
  7. middle=(left + right) / 2;
  8. if(key==nums[middle]) return middle;
  9. if (key < nums[middle]) {
  10. right = middle - 1;
  11. }
  12. else
  13. {
  14. left = middle + 1;
  15. }
  16. return -1;
  17. }

时间复杂度

采用的是分治策略

最坏的情况下两种方式时间复杂度一样:O(log2 N)

最好情况下为O(1)

 空间复杂度

算法的空间复杂度并不是计算实际占用的空间,而是计算整个算法的辅助空间单元的个数
非递归方式:
  由于辅助空间是常数级别的所以:
  空间复杂度是O(1);
递归方式:

 递归的次数和深度都是log2 N,每次所需要的辅助空间都是常数级别的:
 空间复杂度:O(log2N )

整数二分模板:

地址:

LeetCode暑期刷题打卡2019——Week1 二分专题_哔哩哔哩_bilibili

 核心思想是想办法将绿色的左边界或者是红色的右边界包含在内

二分模板一共有两个,分别适用于不同情况。


算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。

版本一:

当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。

模板:

  1. int bsearch_1(int l, int r)
  2. {
  3. while (l < r)
  4. {
  5. int mid = l + r >> 1;//防止int溢出,这里的>>相当于位运算是向右移动一位,相当于是/2
  6. if (check(mid)) r = mid;
  7. else l = mid + 1;
  8. }
  9. return l;
  10. }

版本二:

特别注意:为了防止死循环计算mid时需要加1。


当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,

 模板:

  1. int bsearch_2(int l, int r)
  2. {
  3. while (l < r)
  4. {
  5. int mid = l + r + 1 >> 1;
  6. if (check(mid)) l = mid;
  7. else r = mid - 1;
  8. }
  9. return l;
  10. }
  1. class Solution {
  2. public int[] searchRange(int[] nums, int target) {
  3. int []goal= {-1,-1};
  4. if(nums.length==0) return goal;
  5. //先找元素的第一个位置
  6. int l=0,r=nums.length-1;
  7. while(l<r){
  8. int middle=l+r>>1;
  9. if(nums[middle]>=target) r=middle;
  10. else l=middle+1;
  11. }
  12. //由于我们找的是>=target的数,所以当找到的数!=target,说明数组里面没有target这个数
  13. if(nums[r]!=target) return goal;
  14. int first=r;
  15. //然后找元素的最后一个位置
  16. int left=0,right=nums.length-1;
  17. while(left<right){
  18. int middle=left+right+1>>1;
  19. if(nums[middle]<=target) left=middle;
  20. else right=middle-1;
  21. }
  22. //这里不需要判断<=target的最后一个数是不是=target了,因为如果前面没有target这个数直接返回{-1,-1}
  23. int last=right;
  24. goal[0]=first;
  25. goal[1]=last;
  26. return goal;
  27. }
  28. }

浮点数二分模板:

  1. bool check(double x) {/* ... */} // 检查x是否满足某种性质
  2. double bsearch_3(double l, double r)
  3. { //一般来说如果要保留4位小数eps就取1e-6,保留6位小数就取1e-8
  4. const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
  5. while (r - l > eps)
  6. {
  7. double mid = (l + r) / 2;
  8. //这里不用考虑边界了
  9. if (check(mid)) r = mid;
  10. else l = mid;
  11. }
  12. return l;
  13. }

例题:

浮点数:Acwing790(数的三次方根)--------(二分法)

描述:

 代码:

注意这里的n,l,r都要申明为double

确定范围时,这里的r!=n

因为假如说n=0.001,那么它的三次方根应该=0.1超出了二分的范围

  1. #include <iostream>
  2. using namespace std;
  3. int main(){
  4. double n;
  5. cin>>n;
  6. //为了防止x是小数的情况
  7. double l=-10000,r=10000;
  8. while(r-l>1e-8){
  9. double mid=(l+r)/2;
  10. if(mid*mid*mid<=n) l=mid;
  11. else r=mid;
  12. }
  13. //规定要保留6位小数
  14. printf("%.6f", r);
  15. return 0;
  16. }

Leetcode034描述:

 自己写的题解:

Leetcode034(在排序数组查找元素的第一个位置和最后一个位置)-------------二分法进阶(附带二种模板)_造船大学蒟蒻小菜鸡的博客-CSDN博客

Leetcode069(找平方根):

题解地址:

Leetcode069(找平方根)---------二分法基础运用_造船大学蒟蒻小菜鸡的博客-CSDN博客

Leetcode074(搜索二维矩阵):

单纯考察我们对于二分边界的掌握,以及一维坐标与二维坐标的转换直接上代码:

  1. public boolean searchMatrix(int[][] matrix, int target) {
  2. //这题单纯考察我们对于二分边界的掌握,以及一维坐标与二维坐标的转换
  3. //先将二维转换为一维
  4. int row=matrix.length;//行数
  5. int rank=matrix[0].length;//列数
  6. int l=0,r=row*rank-1;
  7. while(l<r){
  8. int middle=l+r>>1;
  9. if(matrix[middle/rank][middle%rank]>=target) r=middle;
  10. else l=middle+1;
  11. }
  12. if(matrix[l/rank][l%rank]==target) return true;
  13. else return false;
  14. }

Leetcode033、153(搜索旋转排序数组)----------二分法进阶

描述:

 题解地址:

Leetcode033、153(搜索旋转排序数组)----------二分法进阶_造船大学蒟蒻小菜鸡的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/896680
推荐阅读
相关标签
  

闽ICP备14008679号