二分算法
程序或算法的时间复杂度
基本概念
- 一个程序或算法的时间效率,也称“时间复杂度”,有时简称“复杂度”
- 复杂度常用大写字母和小写字母来表示,代表问题的规模
- 时间复杂度是用算法运行过程中,某种时间固定的操作需要被执行的次数和的关系来衡量的。在无序数列中查找某个数,复杂度是。
- 计算复杂度的时候,只统计执行次数最多的( 足够大时)那种固定操作的次数。比如某个算法需要执行加法次,除法次,那么就记其复杂度是的。
- 复杂度有“平均复杂度”和“最坏复杂度”两种。两者可能一致,也可能不一致。一般情况下,只需考虑“平均复杂度”,只有在要求极为严格的情况下,才需要考虑“最坏复杂度”
- 如果复杂度是多个的函数之和,则只关心随增长增长得最快的那个函数
- 复杂度有如下分类
- 常数复杂度: ,时间(操作次数)和问题规模无关
- 对数复杂度:
- 线性复杂度:
- 多项式复杂度:
- 指数复杂度:
- 阶乘复杂度:
- 常见的程序时间复杂度
- 在无序数列中查找某个数(顺序查找):
- 平面上有个点,要求出任意两点之间的距离:
- 插入排序、选择排序、冒泡排序:
- 快速排序:
- 二分查找:
二分查找
**情景导入:**A 心里想一个1~1000之间的数,B来猜,可以问问题,A只能回答是或否,怎么猜才能问的问题次数最少?
- 解决方法:大于500吗?大于750吗?大于625吗?…每次缩小猜测范围到上次的一半,只需要10次
原理及适用情况
- 原理:二分查找每次缩小猜测范围到上次的一半
- 限制:所查找的数组必须是有序的
实现
-
BinarySearch
:在包含size个元素的,从小到大排序的int数组a里面查找元素p,如果找到,则返回元素下标,如果找不到,则返回-1int 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; } } }
-
LowerBound
:包含size个元素的,从小到大排序的int数组a里面查找比给定整数p小的,下标最大的元素。找到则返回其下标,找不到则返回-1int 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 mid = (L+R)/2
:取查找区间正中元素的下标,此种方法可能会溢出- 为了防止
(L+R)
过大溢出:int mid = L+(R-L)/2
基本例题
case 1:二分法求方程的根
**问题描述:**求该方程的根:,若求出的根是,则要求。
-
使用二分法求解该问题是有限制的:方程必须是单调递增或递减的
-
示例代码
#include <iostream> #include <cmath> #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; }
case 2:找一对数
**问题描述:**输入个数,找出其中的两个数,他们之和等于整数,(假定肯定有解)。题中所有的整数都能用int
表示
-
解法一
- 用两重循环,枚举所有的取数方法,复杂度为
-
解法二
- 将数组排序,复杂度是
- 对数组中的每个元素
a[i]
,在数组中二分查找m-a[i]
,看能否找到。复杂度,最坏要查找次,故查找这部分的时间复杂度也是 - 该解法总的复杂度为
-
解法三
- 将数组排序,复杂度是
- 查找的时候,设置两个变量
i
和j
,i
的初值为0
,j
的初值为n-1
。看a[i]+a[j]
,如果大于m
,就让j--
,如果小于m
,就让i++
,直至a[i]+a[j]=m
-
示例代码
#include <iostream> #include <algorithm> // 包含排序的库函数 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(a[mid] == p) { return a[mid]; } else if(a[mid] > p) { R = mid - 1; } else { L = mid + 1; } } return -1; } void solution_one(int a[], int n, int m) { for(int i=0; i<n-1; i++) { for(int j=i+1; j<n; j++) { if(a[i] + a[j] == m) { cout << a[i] <<" "<< a[j] << endl; } } } } void solution_two(int a[], int n, int m) { sort(a,a+n); // 排序 for(int i=0; i<n-1; i++) { int other = m - a[i]; if(BinarySearch(a,n,other) != -1) { cout << a[i] <<" "<< other<<endl; break; } } } void solution_three(int a[], int n, int m) { sort(a,a+n); int i=0, j=n-1; while((a[i]+a[j])!=m) { if(a[i] + a[j] < m) { i++; } if(a[i] + a[j] > m) { j--; } } cout << a[i] <<" "<< a[j] <<endl; } int main() { int a[10] = {9,6,3,8,5,2,7,4,1,0}; solution_three(a,10,13); return 0; }
最后有需要工程文件的朋友可以在评论里说明(记得指明邮箱),小编看到后会第一时间发送到指定邮箱。文章如有不严谨之处,欢迎大家指正!!!
来源:CSDN
作者:A_D_I_D_A_S
链接:https://blog.csdn.net/A_D_I_D_A_S/article/details/104037271