按顺序查找的方法,时间复杂度为O[n];
二分法的时间复杂度为O[logn]。
示例:
在A[n]={1,3,4,6,7,8,10,11,12,15}中找到6和9的下标(失败返回-1)
#include "stdafx.h"
#include <cstdio>
int binaryS(int A[], int left, int right, int x)
{
int mid;
while (left <= right)
{
mid = (left + right) / 2;
if (A[mid] < x)
left = mid + 1;
if (A[mid] > x)
right = mid - 1;
if (A[mid] == x)
return mid;
}
return -1;
}
int main()
{
int A[10] = { 1,3,4,6,7,8,10,11,12,15 };
binaryS(A, 0, 9, 9);
printf("%d\n%d\n", binaryS(A, 0, 9, 6), binaryS(A, 0, 9, 9));
return 0;
}
更进一步的问题: 如果递增序列A中的元素可能重复,那么如何对给定的欲查询元素x,求出序列中第一个大于等于x的元素的位置L以及第一个大于x的元素的位置R,这样元素x在序列中的存在区间就是左闭右开区间。
子问题1:
//A[]为递增序列,x为欲查询的数,函数返回第一个大于等于x的元素的位置
//二分上下界为左闭右闭的[left,right],传入的初值为[0,n]
int lower_bound(int A[], int left, int right, int x)
{
int mid;
while (left < right)//left==right意味着找到唯一位置
{
mid = (left + right) / 2;
if (A[mid] < x)
{
left = mid + 1;
}
else
{
right = mid;
}
}
return left;//返回夹出来的位置
}
子问题2:
int upper_bound(int A[], int left, int right, int x)
{
int mid;
while (left < right)//left==right意味着找到唯一位置
{
mid = (left + right) / 2;
if (A[mid] > x)
{
right = mid;
}
else
{
left = mid + 1;
}
}
return left;//返回夹出来的位置
}
以上两个子问题的区别仅仅在于A[mid]>=0和A[mid]>0(即需要满足的条件)
解决“寻找有序序列第一个满足某条件的元素的位置”问题的固定模板
二分区间是左开右闭区间,循环条件应该是left+1<right,退出循环时有left+1=right成立,使得(left,right]才是唯一位置。而由于变成了左开,left的初值比解的最小取值小1,例如对下标从0开始的序列来说,left和right的取值应该是-1和n返回right
二分法求开方
#include <cstdio>
#include <algorithm>
using namespace std;
double eps = 10e-5;
double f(double x)
{
return x * x;
}
double calSqrt(double a, double b, double out)
{
double left = a, right = b, mid;
while (right - left > eps) //控制精度
{
mid = (left + right) / 2;
if (f(mid) < out)
left = mid;
else
right = mid;
}
return mid;
}
装水问题
f()/calh()是我写的,f0()/solve()是书上的示例
#include <cstdio>
#include<math.h>
#include <algorithm>
using namespace std;
const double eps = 10e-5;
const double Pi = acos(-1.00);
double f(double R, double h)
{
double L = sqrt(R*R - (R - h)*(R - h));
double alpha = asin(L / R);
//double alpha = r * pi;
return (alpha * R*R / 2 - (R - h)*L / 2)*2;//Sr
}
double calh(double R, double r)
{
double SR = R * R*Pi / 2, Sr;
double left = 0, right = R, mid;
double want = SR * r;
while (right - left > eps) //控制精度
{
mid = (left + right) / 2;
if (f(R, mid) < want)
left = mid;
else
right = mid;
}
return mid;
}
double f0(double R, double h)
{
double alpha = 2 * acos((R - h) / R);
double L = 2 * sqrt(R*R - (R - h)*(R - h));
double S1 = alpha * R*R / 2 - L * (R - h) / 2;
double S2 = Pi * R*R / 2;
return S1 / S2;
}
double solve(double R, double r)
{
double left = 0, right = R, mid;
while (right - left > eps)
{
mid = (left + right) / 2;
if (f0(R, mid) > r)
{
right = mid;
}
else {
left = mid;
}
}
return mid;
}
int main()
{
double R, r;
scanf("%lf%lf", &R, &r);
printf("%lf\n%lf", calh(R,r),solve(R,r));
return 0;
}
对于运算式ab %m,用for循环做
for(int i = 0; i < b; i++)
{
ans = ans * a % m;
}
它的时间复杂度是O(b);
用二分幂(快速幂)的思想,它的时间复杂度是O(logb)
递归写法
typedef long long LL;
//求a^b%m 递归写法
LL binaryPow(LL a, LL b, LL m)
{
if (b == 0) return 1;
if (b % 2 == 0)
{
LL temp = binaryPow(a, b / 2, m);
return temp * temp % m;
}
/*if (b % 2 != 0)*/
else
{
LL temp = binaryPow(a, b - 1, m);
return temp * a%m;
}
}
int main()
{
double R, r;
scanf("%lf%lf", &R, &r);
printf("%lf\n%lf", calh(R,r),solve(R,r));
return 0;
}
在上面的代码中,条件if(b%2= =1)可以用if(b&1)代替,因为b&1进行位与操作,判断b的末位是否为1,因此当b为奇数时返回1。这种方式可以提高执行速度。
还要注意当b%2=0时不要直接返回binaryPow(a,b/2%m) * binaryPow(a,b/2%m),会导致复杂度加剧。
细节问题:
- 如果初始时a有可能大于等于m,那么进入函数前就让a对m取模
- 如果m为1,可以直接在函数外部判为0;
迭代写法
typedef long long LL;
//快速幂的迭代写法
LL binaryPow(LL a, LL b, LL m)
{
LL temp = 1;
while(b>0)
{
temp *= a * (b & 1) % m;
/* if (b & 1)
{
temp = a*temp % m;
}*/
b /= 2;
a = a * a%m;
}
return temp;
}
习题
找x
题目描述
输入一个数n,然后输入n个数值各不相同,再输入一个值x,输出这个值在这个数组中的下标(从0开始,若不在数组中则输出-1)。
输入
测试数据有多组,输入n(1<=n<=200),接着输入n个数,然后输入x。
输出
对于每组输入,请输出结果。
样例输入
4
1 2 3 4
3
样例输出
2
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
struct nu {
int id;
int num;
}a[210];
bool cmp(nu a, nu b)
{
return a.num < b.num;
}
//找X
int main()
{
int n;
int x;
int mid;
int L, R;
while (scanf("%d", &n) != EOF)
{
for (int i = 0; i < n; i++)
{
a[i].id = i;
scanf("%d", &a[i].num);
}
scanf("%d", &x);
sort(a, a + n, cmp);
int left = 0, right = n;
while (left<right)
{
mid = (left + right) / 2;
if (a[mid].num >=x)
right = mid;
if(a[mid].num<x)
left = mid+1;
}
if (a[left].num == x)
{
L = left;
//left = 0;
//right = n;
//while (left < right)
//{
// mid = (left + right) / 2;
// if (a[mid].num > x)
// {
// right = mid;
// }
// else
// {
// left = mid + 1;
// }
//}
//R = left-1;
// for (int j = L; j <= R; j++)
// {
// printf("%d", a[j].id);
// if (j != R)
// printf(" ");
// }
// printf("\n");
printf("%d\n", a[L].id);
}
else
printf("-1\n");
}
return 0;
}
还不知道这题做错在哪里
查找
题目描述
输入数组长度 n
输入数组 a[1…n]
输入查找个数m
输入查找数字b[1…m]
输出 YES or NO 查找有则YES 否则NO 。
输入
输入有多组数据。
每组输入n,然后输入n个整数,再输入m,然后再输入m个整数(1<=m<=n<=100)。
输出
如果在n个数组中输出YES否则输出NO。
样例输入
6
3 2 5 4 7 8
2
3 6
样例输出
YES
NO
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
void func(int a[],int b,int n)
{
int left = 0, right = n-1;
int mid;
bool flag = false;
while (left <= right)
{
mid = (left + right) / 2;
if (b > a[mid])
left = mid + 1;
if(b< a[mid])
right = mid - 1;
if (b == a[mid])
{
flag = true;
printf("YES\n");
break;
}
}
if (flag==false)
printf("NO\n");
return;
}
int main()
{
int m, n;
int forwardleft = 0;
int a[110], b[110];
while (scanf("%d", &n) != EOF)
{
for (int q = 0; q < n; q++)
scanf("%d", &a[q]);
sort(a, a + n);
scanf("%d", &m);
for (int q = 0; q < m; q++)
{
scanf("%d", &b[q]);
}
for (int q = 0; q < m; q++)
{
func(a, b[q], n);
}
}
}