二分查找:O(log 2 N)
基础的二分查找只管是否存在,不管是第几个。
算法提升:
在0000000001111111111 找第一个1
找最先满足的要求的位置
int binnary_search1(int *arr, int n) {
int head = 0, tail = n, mid; // n是虚拟位
while (head < tail) {
mid = (head + tail) >> 1;
if (arr[mid] == 0) head = mid + 1;
else tail = mid; // 这个1可能就是第一个1,所以不能mid-1
}
return head == n ? -1 : head; //如果头指向虚拟位,则没找到
}
在1111111111000000000 找最后一个1
找最后一个满足要求的位置
int binnary_search2(int *arr, int n) {
int head = -1, tail = n - 1, mid; // -1是虚拟位
while (head < tail) {
mid = (head + tail + 1) >> 1; // +1是为了上取整
if (arr[mid] == 0) tail = mid - 1;
else head = mid; // 这个1可能就是最后一个1,所以不能mid+1
}
return head; //如果尾指向虚拟位,则没找到
}
oj195
#include <cstdio>
int binary_search(int *arr, int n, int x) {
int min = 0, max = n - 1, mid;
while (min < max) {
mid = (min + max + 1) >> 1;
if (arr[mid] > x) max = mid - 1;
else min = mid;
}
return arr[min];
}
int main() {
int n, m, x;
scanf("%d%d", &n, &m);
int *nrr = new int[n]();
for (int i = 0; i < n; i++) {
scanf("%d", &nrr[i]);
}
for (int i = 0; i < m; i++) {
scanf("%d", &x);
i && printf(" ");
printf("%d", binary_search(nrr, n, x));
}
delete(nrr);
return 0;
}
二分查找实现开平方根(sqrt):
连续问题,离散问题
此问题属于连续问题:
#include <stdio.h>
#include <math.h>
double my_sqrt(double x) {
double min = 0, max = x + 1.0, mid;
#define EPSL 1e-7
while (max - min > EPSL) {
mid = (max + min) / 2.0;
if (mid * mid < x) min = mid;
else max = mid;
}
#undef EPSL
return mid;
}
int main() {
double x;
while (~scanf("%lf", &x)) {
printf("sqrt(%g) = %g\n", x, sqrt(x));
printf("my_sqrt(%g) = %g\n", x, my_sqrt(x));
}
return 0;
}
-
%g用于打印浮点型数据时,会去掉多余的零,至多保留六位有效数字(不同于%e的默认保留小数点后6位)
-
1e-7是10的-7次方
-
max = x + 1.0是为了:如果开小于1的平方根的话,会导致寻找的数字根本不在范围。所以加上1,即可包含进查找范围
------------------------------------------------
牛顿迭代算法(logN):是C语言sqrt底层实现的算法
牛顿迭代解决高阶(单调)方程求根问题
方程必须连续可导才能用牛顿迭代法
用牛顿迭代设计sqrt:
x = √n
x * x = n
原函数F(x) = x * x - n表示x与√n之间的误差值
f(x) = 2x表示斜率,用于求下一个迭代x的值
-----------------------------
如求√5: f(x) = x^2 - 5
随便取一点初始值x0, 取x0 = 5
则点(x0, f(x0))的切线为f'(x0)
切线方程:点斜式y-f(x0)=f'(x0)(x-x0),
其与x轴交于(x1, 0),带入切线方程得:
0-f(x0)=f'(x0)(x1-x0)
解得x1 = x0 - f(x0) / f'(x0)
即为1次迭代,下一次取(x1, f(x1)),取切线方程,交于x轴为x2,然后再取(x2, f(x2))...
直到x * x 与 n之间的差值即F(x, n)的返回值小于误差EPSL,输出其x为平方根。

#include <stdio.h>
#include <math.h>
// 原函数f(x) = x^2 - n, 因为x^2 = n, x=根号n
double F(double x, double n) {
return x * x - n;
}
// 导函数f'(x) = 2x
double f(double x) {
return 2 * x;
}
double NewTon(double (*F)(double, double), double (*f)(double), double n) {
double x = n;
#define EPSL 1e-7
while (fabs(F(x, n)) > EPSL) {
x -= F(x, n) / f(x);
}
#undef EPSL
return x;
}
double my_sqrt(double n) {
return NewTon(F, f, n);
}
int main() {
double n;
while(~scanf("%lf", &n)) {
printf("sqrt(%g) = %g\n", n, sqrt(n));
printf("my_sqrt(%g) = %g\n", n, my_sqrt(n));
}
return 0;
}
PS:最快的sqrt是O(1)的,雷神之锤3的工程师设计的