当前位置: 首页 > news >正文

算法-二分查找

程序或算法的时间复杂度

一个程序或算法的时间效率,也称“时间复杂度”,有时简称“复杂度”

复杂度常用大的字母O和小写字母n来表示,比如O(n),O(n2)等。n代表问题 的规模

时间复杂度是用算法运行过程中,某种时间固定的操作需要被执行的次数和n 的关系来度量的。在无序数列中查找某个数,复杂度是O(n)

计算复杂度的时候,只统计执行次数最多的(n足够大时)那种固定操作的次数 。比如某个算法需要执行加法n的平方 ,除法n次,那么就记其复杂度是O((n的平方)的。

复杂度有“平均复杂度”和“最坏复杂度”两种。 两者可能一致,也可能不一致。

二分查找函数

写一个函数BinarySeach,在包含size个元素的、从小到大排序的int数组a里查找元素 p,如果找到,则返回元素下标,如果找不到,则返回-1。要求复杂度O(log(n))。

写一个函数LowerBound,在包含size个元素的、从小到大排序的int数组a里查找比给 定整数p小的,下标最大的元素。找到则返回其下标,找不到则返回-1

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int BinarySearch(int a[],int size,int p)
{
    int L = 0; //查找区间的左端点
    int R = size - 1; //查找区间的右端点
    while( L <= R) { //如果查找区间不为空就继续查找
        int mid = L+(R-L)/2; //取查找区间正中元素的下标
        if( p == a[mid] )
            return mid;
        else if( p > a[mid])
            L = mid + 1; //设置新的查找区间的左端点
        else
            R = mid - 1; //设置新的查找区间的右端点
    }
    return -1;
} //复杂度O(log(n))
int LowerBound(int a[],int size,int p)  //复杂度O(log(n))
{
    int L = 0; //查找区间的左端点
    int R = size - 1; //查找区间的右端点
    int lastPos = -1; //到目前为止找到的最优解
    while( L <= R) { //如果查找区间不为空就继续查找
        int mid = L+(R-L)/2; //取查找区间正中元素的下标
        if(a[mid]>= p)
            R = mid - 1;
        else {
            lastPos = mid;
            L = mid+1;
        }
    }
    return lastPos;
    }
int main(){
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    int p = 6;
    int index = BinarySearch(a,10,p);
    int index1= LowerBound(a,10,p);
    if(index != -1){
        printf("The index of %d is %d\n",p,index);
    }else{
        printf("Not found\n");
    }
    if(index1 != -1){
        printf("The index of %d is %d\n",p,index1);
    }else{
        printf("Not found\n");
    }
}

为了防止(L+R)过大溢出: int mid = L+(R-L)/2;

The index of 6 is 5
The index of 6 is 4

二分法求方程的根

求下面方程的一个根:f(x) = x3-5x2+10x-80 = 0

若求出的根是a,则要求|f(a)| <= 10-6

解法:对f(x)求导,得f'(x)=3x2-10x+10。由一元二次方程求根公式知方程 f'(x)= 0 无解,因此f'(x)恒大于0。故f(x)是单调递增的。易知f(0) < 0且 f(100)>0,所以区间[0,100]内必然有且只有一个根。由于f(x)在[0,100]内是单 调的,所以可以用二分的办法在区间[0,100]中寻找根

绝对值函数 fabs()忽略输入数据的负号,fabs(x)表示计算变量x的绝对值.

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
double EPS = 1e-6;
double f(double x) { return  x*x*x - 5*x*x + 10*x - 80; }
int main()  {
    double root, x1 = 0, x2 = 100,y;
    root = x1+(x2-x1)/2;
    int triedTimes = 1; //记录一共尝试多少次,对求根来说不是必须的
    y = f(root);
    while( fabs(y) > EPS) {
        if( y > 0 )
            x2 = root;
        else
            x1 = root;
        root = x1+(x2 - x1)/2;
        y = f(root);
        triedTimes ++;
    }
    printf("%.8f\n",root);
    printf("%d",triedTimes);
    return 0;
    }

5.70508593
32


寻找指定和的整数对

输入n ( n<= 100,000)个整数,找出其中的两个数,它们之和等于整数m(假定肯 定有解)。题中所有整数都能用int 表示。

解法1:用两重循环,枚举所有的取数方法,复杂度是O(n2)的。

for(int i = 0;i < n-1; ++i)
    for(int j = i + 1; j < n; ++j) 
        if( a[i]+a[j] == m)
             break;
#include <iostream>
#include <vector>
using namespace std;

// 函数:findTwoNumbers
// 功能:在数组nums中找到两个数,使它们的和等于m
// 参数:nums:数组;m:目标和
void findTwoNumbers(vector<int>& nums, int m) {
    int n = nums.size(); // 获取数组长度
    for (int i = 0; i < n; i++) { // 遍历数组
        for (int j = i + 1; j < n; j++) { // 从i+1开始遍历数组
            if (nums[i] + nums[j] == m) { // 如果找到两个数的和等于m
                cout << nums[i] << " " << nums[j] << endl; // 输出这两个数
                return; // 结束函数
            }
        }
    }
    cout << "No solution" << endl; // 如果没有找到,输出"No solution"
}

int main() {
    vector<int> nums = {1, 2, 3, 4, 5}; // 定义数组
    int m = 7; // 定义目标和
    findTwoNumbers(nums, m); // 调用函数
    return 0;
}

解法2:

1 将数组排序,复杂度是O(n×log(n))

2 对数组中的每个元素a[i],在数组中二分查找m-a[i],看能否找到。复杂度log(n),最 坏要查找n-2次,所以查找这部分的复杂度也是O(n×log(n))

这种解法总的复杂度是O(n×log(n))的。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void findTwoNumbers(vector<int>& nums, int target) {
    // 对数组进行排序
    sort(nums.begin(), nums.end());

    // 遍历数组中的每个元素
    for (int i = 0; i < nums.size() - 1; ++i) {
        // 在排序后的数组中使用二分查找来查找m-nums[i]
        int left = i + 1, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target - nums[i]) {
                cout << "找到的两个数是: " << nums[i] << " 和 " << nums[mid] << endl;
                return;
            } else if (nums[mid] < target - nums[i]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }

    // 如果没有找到,输出提示信息
    cout << "没有找到符合条件的两个数" << endl;
}

int main() {
    int n, m;
    cout << "请输入整数n: ";
    cin >> n;
    cout << "请输入整数m: ";
    cin >> m;

    vector<int> nums(n);
    cout << "请输入" << n << "个整数: ";
    for (int i = 0; i < n; ++i) {
        cin >> nums[i];
    }

    findTwoNumbers(nums, m);

    return 0;
}

解法3

1 将数组排序,复杂度是O(n×log(n))

2 查找的时候,设置两个变量i和j,i初值是0,j初值是n-1.看a[i]+a[j],如果大于m,就让j 减1,如果小于m,就让i加1,直至a[i]+a[j]=m。

这种解法总的复杂度是O(n×log(n))的。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 函数用于在数组中找到两个数,使它们的和等于目标值
void findTwoNumbers(vector<int>& nums, int target) {
    // 对数组进行排序
    sort(nums.begin(), nums.end());
    // 定义左右指针
    int left = 0, right = nums.size() - 1;
    // 当左右指针没有相遇时,继续循环
    while (left < right) {
        // 计算左右指针指向的数的和
        int sum = nums[left] + nums[right];
        // 如果和等于目标值,输出结果
        if (sum == target) {
            cout << "The two numbers are: " << nums[left] << " and " << nums[right] << endl;
            return;
        // 如果和小于目标值,左指针右移
        } else if (sum < target) {
            left++;
        // 如果和大于目标值,右指针左移
        } else {
            right--;
        }
    }
    cout << "No two numbers sum up to " << target << endl;
}
int main() {
    int n, m;
    cin >> n >> m;

    vector<int> nums(n);
    for (int i = 0; i < n; ++i) {
        cin >> nums[i];
    }
    findTwoNumbers(nums, m);
    return 0;
}

1. `sort` 函数:这是 C++ 标准库 `<algorithm>` 头文件中的一个函数,用于对容器中的元素进行排序。

2. `nums.begin():这是容器的起始迭代器,指向容器中的第一个元素。

3. `nums.end()`:这是容器的结束迭代器,指向容器中的最后一个元素的下一个位置。`end()` 是容器类的一个成员函数,返回一个迭代器,指向容器中的最后一个元素的下一个位置。

综合起来,sort(nums.begin(), nums.end()); 的作用是对 nums 容器中的元素进行升序排序。排序完成后,nums 中的元素将按照从小到大的顺序排列。

std::vector 是 C++ 标准库中的一个动态数组类,它提供了动态数组的功能,可以在运行时动态地改变大小。std::vector 位于 <vector> 头文件中。

5 10 2 7 11 15 3

  • 5 是数组的长度,表示数组中有 5 个元素。
  • 10 是目标值,表示我们需要找到两个数,使它们的和等于 10。
  • 2 7 11 15 3 是数组的元素。

Aggressive cows

农夫John 建造了一座很长的畜栏,它包括N (2≤N≤100,000)个隔间,这 些小隔间的位置为x0,...,xN-1 (0≤xi≤1,000,000,000,均为整数,各不相同).

John的C (2≤C≤N)头牛每头分到一个隔间。牛都希望互相离得远点省得互 相打扰。怎样才能使任意两头牛之间的最小距离尽可能的大,这个最大的 最小距离是多少呢?

解法1:

先得到排序后的隔间坐标x0,...,xN-1

从1,000,000,000/C到1依次尝试这个“最大的最近距离”D,找到的 第一个可行的就是答案。

尝试方法:

1 第1头牛放在x0

2 若第k头牛放在xi ,则找到xi+1到xN-1中第一个位于[xi+D, 1,000,000,000]中的Xj ,第k+1头牛放在Xj。找不到这样的Xj,则D=D-1,转1)再试 29

若所有牛都能放下,则D即答案

复杂度1,000,000,000/C *N,即 1,000,000,000, 超时

#include <iostream>
#include <vector>
#include <algorithm>

int max_min_distance(int N, int C, std::vector<int>& positions) {
    std::sort(positions.begin(), positions.end());  // 对隔间位置进行排序
    int left = 0, right = positions[N-1] - positions[0];  // 初始化二分查找的左右边界

    while (left < right) {
        int mid = (left + right + 1) / 2;  // 计算中间值
        int count = 1;  // 初始化牛的数量
        int last_position = positions[0];  // 初始化上一个牛的位置

        for (int i = 1; i < N; i++) {
            if (positions[i] - last_position >= mid) {
                count += 1;
                last_position = positions[i];
            }
            if (count >= C) {
                break;
            }
        }

        if (count >= C) {
            left = mid;  // 如果牛的数量大于等于C,说明mid是一个可行的最小距离
        } else {
            right = mid - 1;  // 否则,将右边界更新为mid - 1
        }
    }

    return left;
}

int main() {
    int N = 5;
    int C = 3;
    std::vector<int> positions = {1, 2, 8, 15, 20};

    // 调用函数并输出结果
    std::cout << max_min_distance(N, C, positions) << std::endl;  // 输出:7

    return 0;
}

7

解法2:

先得到排序后的隔间坐标x0,...,xN-1

在[L,R]内用二分法尝试“最大最近距离”D = (L+R)/2 (L,R初值为 [1, 1,000,000,000/C]

若D可行,则记住该D,然后在新[L,R]中继续尝试(L= D+1)

若D不可行,则在新[L,R]中继续尝试(R= D-1)

复杂度log(1,000,000,000/C) * N

#include <iostream>
#include <vector>
#include <algorithm>

int max_min_distance(int N, int C, std::vector<int>& positions) {
    std::sort(positions.begin(), positions.end());  // 对隔间位置进行排序
    int L = 1, R = positions[N-1] / C;  // 初始化二分查找的左右边界

    while (L < R) {
        int D = (L + R) / 2;  // 计算中间值
        int count = 1;  // 初始化牛的数量
        int last_position = positions[0];  // 初始化上一个牛的位置

        for (int i = 1; i < N; i++) {
            if (positions[i] - last_position >= D) {
                count += 1;
                last_position = positions[i];
            }
            if (count >= C) {
                break;
            }
        }

        if (count >= C) {
            L = D + 1;  // 如果牛的数量大于等于C,说明D是一个可行的最小距离
        } else {
            R = D - 1;  // 否则,将右边界更新为D - 1
        }
    }

    return L;
}

int main() {
    int N = 5;
    int C = 3;
    std::vector<int> positions = {1, 2, 8, 15, 20};

    // 调用函数并输出结果
    std::cout << max_min_distance(N, C, positions) << std::endl;  // 输出:7

    return 0;
}

6

相关文章:

  • (番外篇一)学习webgl是先从现有的框架还是直接从底层开始学?
  • 小米15怎么录音转文字?录音转文字技巧软件、分享
  • LarkXR用户调研洞察:2024-2025年度平行云客户满意度报告
  • 事务的四大特性(ACID)详解
  • Spring 管理线程并实现Runnable接口的任务
  • Zabbix实践教程: ssl证书有效期监控
  • Python 常用标准库功能与用法指南
  • Linux dma的使用与理解
  • 【PPO】小白的强化学习算法笔记
  • 一文讲清楚Python中函数和类区别和联系
  • 【更新中】【React】基础版React + Redux实现教程,自定义redux库和react-redux库
  • 【vue】vue + vant实现上传图片添加水印
  • 25、web前端开发之CSS3(二)
  • 1.6 循环嵌套
  • USB总线示波器采集卡--2 通道,10G采样
  • DML 数据操纵语言学习笔记
  • 【NLP 48、大语言模型的神秘力量 —— ICL:in context learning】
  • ffmpeg-将多个视频切片成一个新的视频
  • 智能化集成管理系统的核心特点与发展趋势
  • 26考研——树与二叉树_树与二叉树的应用(5)
  • 怎么设置网站标题/珠海网络推广公司
  • 怎么在vps上做网站/网站排名优化外包公司
  • 会计上网站建设做什么费用/百度app官网下载
  • wordpress 文章分类/seo排名推广工具
  • 沈阳科技网站建设/郑州网站建设外包
  • 保定网站关键词优化/网站运营推广的方法有哪些