二分查找
对于递增序列,顺序查找的时间复杂度为
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为半圆的面积)
#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;
}
使用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;
}