• 算法笔记--二分


    二分查找

    对于递增序列,顺序查找的时间复杂度为O(n),如果序列太大,就很难承受

    在有序序列中,利用二分查找可以有效减少时间复杂度,其时间复杂度为O(logn)

    对于递增数列,二分查找如下:

    #include<cstdio>
    int binarySearch(int myArray[], int left, int right, int x){
        while(left <= right){
            int mid = (left + right) / 2;
            if(myArray[mid] == x)return mid;
            else if(myArray[mid] < x)
            {
                left = mid + 1;
            }
            else
                right = mid - 1;
        }
        return -1;
    }
    
    int main(){
        int myArray[] = {1, 2, 3, 7, 10, 12};
        int x = 3;
        int site = binarySearch(myArray, 0, 5, x);
        printf("the position of %d is %d ", x, site);
    }
    

    注意:当二分上界超过int型数据范围的一半,那么当欲查询元素在序列比较靠前的位置时,(left + right)可能会超过int而导致溢出,此时,应该使用mid = left + (right - left) / 2来代替避免溢出

    对于重复元素的递增数列,求出序列中第一个大于等于x的元素的位置L以及第一个大于x的元素的位置R,这样元素X在序列中的存在区间就是左闭右开区间 [L, R)

    如:对于 {1, 3, 3, 3, 6}

    查询3,L = 1, R = 4; 查询5,L = R = 4; 查询6,L = 4, R = 5; 查询8,L = R = 5

    int solve(int letf, int right){
        int mid;
        while(left < right){ 		// letf == right 意味着找到唯一位置
            mid = (left + right) / 2;
            if(条件成立){
                right = mid;
            }esle{
                left = mid + 1;
            }
        }
        return letf;
    }
    

    对于条件成立

    • 当寻找大于等于第一个数的时,为mid >= x
    • 当寻找第一个大于的数时,为mid > x
    • 注意推导即可

    二分法拓展

    计算(√2)的近似值

    #include<cstdio>
    double f(double x){
        return x * x;
    }
    
    int main(){
        const double eps = 1e-5;
        double left = 1;
        double right = 2;
        double mid;
        while(right - left > eps){
            mid = left + (right - left) / 2;
            if(f(mid) > 2)
                right = mid;
            else
                left = mid;
        }
        printf("%.5f", mid);
        return 0;
    }
    

    该例使用与求f(x)的根,逼近√2相当于求(f(x) = x^2 - 2 = 0)的零点

    装水问题

    求 $S_1 / S_2 的为 r $, (S_1为水的面积), (S_2为半圆的面积)

    Snipaste_2020-02-26_15-31-05

    #include<cstdio>
    #include<cmath>
    const double PI = acos(-1.0);
    double f(int h, int R){
        double alpha = 2 * acos((R - h) / R);
        double L = 2 * sqrt(R * R - (R - h) * (R - h));
        double S1 = 1/2 * R * R * alpha - 1/2 * L * (R - h);
        double S2 = PI * R * R / 2;
        return S1 / S2;
    }
    
    int main(){
        double R, r;
        double mid;
        scanf("%lf %lf", &R, &r);
        double left = 0;
        double right = R;
        while(right - left > 1e-5){
            mid = left + (right - left) / 2;
            if(f(mid, R) > r){
                right = mid;
            }else{
                left = mid;
            }
        }
        printf("当 r = %.2f 时,注水高度为:%.2f", r, mid);
        return 0;
    }
    

    快速幂

    学习快速幂首先要明白模运算规则

    基本运算:
    (a + b) % p = (a % p + b % p) % p
    (a - b) % p = (a % p - b % p) % p
    (a * b) % p = (a % p * b % p) % p
    a^b % p = ((a % p)^b) % p
    结合律:
    ((a + b) % p + c) % p = (a + (b + c) % p) % p
    ((a * b) % p * c) % p = (a * (b * c) % p) % p
    

    给的三个正整数(a、b、m), ((a < 10^9, b < 10^6, 1 < m < 10^9), 求(a^b % m))

    如果使用for循环,其时间复杂度为O(b)

    #include<cstdio>
    typedef long long LL;
    LL pow(LL a, LL b, LL m){
        LL ans = 1;
        for(int i = 0; i < b; i++){
            ans = ans * a % m;		// 见下面图推导
        }
        return ans;
    }
    
    int main(){
        LL x = pow(3, 10, 5);
        printf("%ld",x);
        return 0;
    }
    
    88EBD90BF9F388A7B77F6DA275505733

    使用for循环在指数很大的情况下是不行的,所以已经使用快速幂的做法,也称为二分幂

    • 如果b是奇数,那么有(a^b = a * a^{b - 1})
    • 如果b是偶数,那么有(a^b = a^{b/2} * a^{b/2})

    快速幂的递归写法:时间复杂度为O(logb)

    typedef long long LL;
    LL binaryPow(LL a, LL b, LL m){
        if(b == 0) return 1;
        else if(b & 1) return a * binaryPow(a, b - 1, m);
        else{
            LL mul = binaryPow(a, b / 2, m);
            return mul * mul % m;
        }
    }
    

    if(b & 1)用于判断b的末位是否为1,等价于b % 2 == 1

    快速幂的迭代写法

    如13的二进制为1101,等价于(a^13 = a^{8+4+1} = a^8×a^4×a^1)

    typedef long long LL;
    LL binaryPow(LL a, LL b, LL m){
        LL ans = 1;
        while(b > 1){
            if(b & 1){				// 如果b的二进制末尾为1
                ans = ans * a % m;	// 令ans累积上a
            }
            a = a * a % m;
            b >> 1;					// 将b的二进制右移1位,即 b = b >> 1或 b = b / 2 
        }
        return ans;
    }
    
  • 相关阅读:
    RAS ADSL Dial 宽带 拨号
    安全文章收藏
    android系统下可以实时语音对讲的软件应用。
    ArcGIS 10 Engine 我今天的研究战果
    arcgis sample代码之SOE示例代码PageLayout REST Server Object Extension 的源码分析
    eclipse之缩进shift+Tab无法使用的情况
    arcgis sample代码之SOE示例代码Length Calculator Server Object Extension的源码分析
    解决:eclipse双击无法打开工程目录了
    修改注册表,在运行程序后加一个pause;方便查看一闪而过的程序|打开加加 软件简介
    转:2020年,中国将会是一个非常穷的国家!【无论如何我都要转,里面有许多我所想的】
  • 原文地址:https://www.cnblogs.com/zgqcn/p/12451872.html
Copyright © 2020-2023  润新知