结合时间复杂度浅谈二分法的好处, 并分享部分二分题目(将持续更新题目,绝对值你一个收藏)

前言

笔者虽然刷的算法题不多,但是笔者也敢说,二分法真的是一种很优越的算法,使用上限极高的那种,正因如此,笔者才想浅谈一下二分法.

封面是我很喜欢的一个游戏角色,不知道有没有老gal玩家知道!

什么是二分法?

枚举查找即顺序查找,实现原理是逐个比较数组 a[0:n-1] 中的元素,直到找到元素 x 或搜索整个数组后确定 x 不在其中。最坏情况下需要比较 N 次,时间复杂度是 O(n),属于线性阶算法。

而二分查找是一种折半查找方法。该方法将 N 个元素分成大致相等的两部分,选取中间元素与查找的元素进行比较。如果相等,则查找成功;如果查找元素小于中间元素,则在左半区继续查找;如果查找元素大于中间元素,则在右半区继续查找。每次都将范围缩小至原来的一半,因此时间复杂度是 O(log2n)。需要注意的是,二分查找的前提是数组有序,一般是从小到大排列。

相信大家伙学c语言的时候就接触过了,笔者上一行代码给大家看看

C++ 版本

// C++  的版本
while (low < high)
{
    int mid = (low + high) / 2;
    if (a[mid] >= x)
        high = mid;

    else
        low = mid + 1;
}

while (low < high)
{
    int mid = (low + high + 1) / 2;

    if (a[mid] <= x)
        low = mid;

    else
        high = mid - 1;
}

java版本


while (low < high) {

    int mid = (low + high) / 2;

    if (a[mid] >= x)
      high= mid;

  else
      low = mid + 1;
}

while (low < high) {

    int mid = (low + high + 1) / 2;

    if (a[mid] <= x)
      low = mid;

  else
      high = mid - 1;

}

看得出来,真的没什么区别(哈哈哈!) 毕竟语言有界,算法无界!

时间复杂度

接下来,我们来分享它的时间复杂度

所谓时间复杂度,说白了,就是一个代码算法执行的次数

那么二分的时间复杂度怎么算?

答:O(log2n)

如图所示(有点丑别介意)

我们假设最坏情况,需要查询(执行)y次才能找到我们要的数,那么每次都是二分,2^y 就能代表总数N,所以执行y=log2n  即 时间复杂度为O(log2n)

与n相比O(log2n) 不就是很大的提升了吗?

刚刚分享的是二分整数,还有精度较高的写法,以下代码来自 

风骨散人Chiam-CSDN博客

我也上过这位老师的课,给大家推荐下!!!!

//模版一:实数域二分,设置eps法

//令 eps 为小于题目精度一个数即可。比如题目说保留4位小数,0.0001 这种的。那么 eps 就可以设置为五位小数的任意一个数 0.00001- 0.00009 等等都可以。

//一般为了保证精度我们选取精度/100 的那个小数,即设置 eps= 0.0001/100 =1e-6

while (l + eps < r)
{
    double mid = (l + r) / 2;

    if (pd(mid))
        r = mid;
    else
        l = mid;
}

//模版二:实数域二分,规定循环次数法
//通过循环一定次数达到精度要求,这个一般 log2N < 精度即可。N 为循环次数,在不超过时间复杂度的情况下,可以选择给 N 乘一个系数使得精度更高。

    for (int i = 0; i < 100; i++)
{

    double mid = (l + r) / 2;
    if (pd(mid))
        r = mid;
    else
        l = mid;
}

部分题目分享

笔者接下来分享写过的题目

题目一  —— 来自蓝桥的计算方程

11.计算方程【算法赛】 - 蓝桥云课 (lanqiao.cn)

给大家伙看看题目

计算机又不会解方程,那我们咋办嘛?

就好比高中的选择压轴题,我们套答案!

怎么套! 一个个列举呗

怎么列举?难道从1开始一个个去列举吗?那要什么时候列举的出来?

那咋办吗? 这个时候就需要二分了,找到适合的答案,且以较低的复杂度

C++

#include <iostream>
#include <cmath>

#define ll long long

int k, m;

int check(int x)
{
    if (sqrt(x) + floor(log(x) / log(k)) - m > 0)
        return 1;
    return 0;
}

void solve()
{
    std::cin >> k >> m;
    int l = 1, r = 1000000000;
    while (l < r)
    {
        int mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    std::cout << l << std::endl;
}

int main()
{
    int t;
    std::cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

Java
 

import java.util.Scanner;

public class Main {

    static int k, m;

    static int check(int x) {
        if (Math.sqrt(x) + Math.floor(Math.log(x) / Math.log(k)) - m > 0)
            return 1;
        return 0;
    }

    static void solve() {
        Scanner scanner = new Scanner(System.in);
        k = scanner.nextInt();
        m = scanner.nextInt();
        int l = 1, r = 1000000000;
        while (l < r) {
            int mid = (l + r) / 2;
            if (check(mid))
                r = mid;
            else
                l = mid + 1;
        }
        System.out.println(l);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        while (t-- > 0) {
            solve();
        }
        scanner.close();
    }
}

套公式走

耗时不太长!!!

题目二——来自洛谷的银行贷款

P1163 银行贷款 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

本人题解如下

#include <bits/stdc++.h>

using namespace std;

double eps = 1e-4; // 精度
double a, b, c;

int main() {
    cin >> a >> b >> c;
    double l = 0;
    double r = 100; // 调整搜索区间上限为100
    while (l + eps < r) {
        double mid = (l + r) / 2;
        double num = a;
        // 表示需要还的钱
        for (int i = 1; i <= c; i++) {
            num = num * (1 + mid / 100) - b;
        }
        if (num > 0) {
            r = mid; // 如果剩余金额大于0,说明利率太高,调低上限
        } else {
            l = mid; // 否则,调高下限
        }
    }

    printf("%.1lf", l); // 输出结果,保留一位小数
    return 0;
}

二分求利率,但是但是,这题笔者并没有AC

           具体问题我还要再看看,所以以后会更新的.

题目三——(冶炼金属)

0冶炼金属 - 蓝桥云课 (lanqiao.cn)

本质上也是二分找答案,不过要分两次, 一次找最大值,一次找最小值

代码如下

#include <iostream>
using namespace std;

int a[10000];
int b[10000];
int n;



int check1(int x) {
    for (int i = 1; i <= n; i++) {
        if (b[i] < a[i] / x) {
            return 1;
        }
    }
    return 0;
}

int check2(int y) {
    for (int i = 1; i <= n; i++) {
        if (b[i] > a[i] / y) {
            return 1;
        }
    }
    return 0;
}

int main() {
    // 请在此输入您的代码
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i] >> b[i];
    }
    int lmin = 0;
    int rmin = 1000000000;
    while (lmin + 1 != rmin) {
        int mid = (lmin + rmin) / 2;
        if (check1(mid)) {
            lmin = mid;
        } else {
            rmin = mid;
        }
    }
    int lmax = 0;
    int rmax = 1000000000;

    while (lmax + 1 != rmax) {
        int mid2 = (lmax + rmax) / 2;
        if (check2(mid2)) {
            rmax = mid2;
        } else {
            lmax = mid2;
        }
    }


    cout << rmin << ' ' << rmax - 1;

    return 0;
}

找最小值的二分

int check1(int x) {
    for (int i = 1; i <= n; i++) {
        if (b[i] < a[i] / x) {
            return 1;
        }
    }
    return 0;
}

找最大值的二分

int check2(int y) {
    for (int i = 1; i <= n; i++) {
        if (b[i] > a[i] / y) {
            return 1;
        }
    }
    return 0;
}

然后就可以得到答案了

题目四——分巧克力

分巧克力 - 蓝桥云课 (lanqiao.cn)

也是典型的,二分答案, 题解如下

#include <iostream>
# include<bits/stdc++.h>
using namespace std;
const int N=1e6;
  int n,k;
int hi[N];
int wi[N];

//1.形状相同
//2.大小相等  
//3.最大边长
//4.就是二分
int pd(int x)
{
  int sum=0;
  for(int j=1;j<=n;j++)
  // 通过循环,先看从第一个巧克力开始,到第n个巧克力,算出在x边长下能有多少巧克力给分出来
  {
    sum=sum+((hi[j]/x)*(wi[j]/x));
  }
  if(sum>=k)
  // 分的数量大于 指定数量,就可以增加边长了
  {
    return 1;
  }
  else
  // 不然的话,就减小边长
  {
    return 0;
  }
}

int main()
{
  // 请在此输入您的代码
  cin>>n>>k;
  for(int i=1;i<=n;i++)
  {
    cin>>hi[i]>>wi[i];
  }
  int r;
  for(int i=1;i<=n;i++)
  {
    r=max(hi[i],r);
    r=max(wi[i],r);
  }

  // 这一步,求得是 r的最大值
  int l=0;
  int ans=0;
  while(l+1!=r)
  {
      ans=(l+r)/2;
      if(pd(ans))
      {
        l=ans;
      }
      else
      {
        r=ans;
      }
  }
   cout<<l<<endl;
  return 0;
}

注释已经帮大伙写好了!!!!!

结尾

今天看书,又看到了关于时间复杂度的内容,故而有感而发,写下这篇博客,赏个脸就点赞呗,谢谢谢谢谢!!!!!!!

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值