目录
第2章 算法分析
2.1 数学基础
1. T(N)=O(f(N)) T(N)的增长率小于或等于f(N)的增长率 T(N)是以不快于f(N)的速度增长
2. T(N)=Ω(f(N)) T(N)的增长率大于或等于f(N)的增长率
3. T(N)=Θ(h(N)) T(N)的增长率等于f(N)的增长率
上界(upper bound)
下界(lower bound)
大O表示法
常数因子、低阶项,可以忽略、去掉
2.3 要分析的问题
Tavg(N) :算法对输入大小为N所花费的平均情形的运行时间
Tworst(N) :算法对输入大小为N所花费的最坏情形的运行时间
程序是算法以一种特殊编程语言的实现
一般来说,若无其他的指标,则所要求的量是最坏情况的运行时间,它为所有的输入提供了一个界限,包括特别坏的输入
最大的子序列和问题
2.4 运行时间计算
不存在特定的时间单位
计算大O运行时间
抛弃前导的常数
抛弃低阶项
大O是一个上界,程序可以提前结束,但绝不能错后
2.4.3 最大子序列和问题的求解
分治:
分:把问题分成两个大致相同的子问题,然后递归地对它们求解
治:将两个子问题的解修补到一起,并可能再做些小量的附加工作,最后得到整个问题的解
分治法解决最大子序列和问题,算法时间复杂度为:O(NlogN)
//fig2.8_P54.cpp
//线性时间最大相连子序列的解法(最佳解法)
#include <iostream>
#include <vector>
using namespace std;
//1.j代表当前序列的终点,i代表当前序列的终点
//2.如果我们不需要知道最佳子序列在哪里,那么i的使用可以从程序上被优化掉,不过在设计算法的时候还是假设i是需要的
//3.如果arr[i]是负的,那么它不可能代表最佳子序列的起点,因为任何包含arr[i]作为起点的子序列都可以通过arr[i+1]作起点而得到改进
//4.同理,任何负的子序列都不可能是最优子序列的前缀
int maxSubSum4(const vector<int> &a)
{
int maxSum = 0, thisSum = 0;
for (int j = 0; j < a.size(); ++j)
{
thisSum += a[j];
if (thisSum > maxSum)
maxSum = thisSum;
else if (thisSum < 0) //
thisSum = 0;
}
return maxSum;
}
int main()
{
vector<int> arr{-2, 11, -4, 13, -5, -2}; //答案为20, arr[1]到arr[3]
cout << maxSubSum4(arr) << endl;
return 0;
}
2.4.4 运行时间中的对数
分析算法最混乱的方面大概几种在对数上面
某些分治算法将以O(NlogN)时间运行
对数最常出现的规律可以概括为下列一般法则
1. 如果一个算法用常数(O(1))时间将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)
2. 如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)
介绍3个具有对数特点的例子
1. 二分查找/折半查找
fig2.9_P55_binarySearch.cpp
//fig2.9_P55_binarySearch.cpp
#include <iostream>
#include <vector>
using namespace std;
const int NOT_FOUND = -1;
/**
* Performs the standard binary search using two comparisons per level.
* Returns index where item is found or -1 if not found
*/
template <typename Comparable>
int binarySearch(const vector<Comparable> &a, const Comparable &x)
{
int low=0,high=a.size()-1;
while(low<=high)
{
int mid=(low+high)/2;
if(a[mid]<x)
low=mid+1;
else if(a[mid]>x)
high=mid-1;
else
return mid;//Found
}
return NOT_FOUND;
}
int main()
{
const int SIZE=8;
vector<int> a(SIZE);
for(int i=0;i<SIZE;++i)
a[i]=i*2;
for(int j=0;j<SIZE*2;++j)
cout<<"FOUND "<<j<<" at"<<binarySearch(a,j)<<endl;
return 0;
}
2. 计算最大公因数(gcd)的欧几里得算法
//fig2.10_P56_gcd.cpp
//fig2.10_P56_gcd.cpp
//计算最大公因数(gcd)的欧几里得算法
//通过连续计算余数知道余数是0位为止,最后的非零余数就是最大公因数
#include <iostream>
using namespace std;
long gcd(long long m, long long n)
{
while (n != 0)
{
long long rem = m % n;
m = n;
n = rem;
}
return m;
}
// Test program
int main()
{
cout << "gcd( 45, 35 ) = " << gcd(45, 35) << endl;
cout << "gcd( 1989, 1590 ) = " << gcd(1989, 1590) << endl;
return 0;
}
3. 幂运算,返回x的n次方
//fig2.11_P57_pow.cpp
//幂运算,返回x的n次方
#include <iostream>
using namespace std;
bool isEven(int n) //判断是否是偶数,是:true
{
return (n % 2 == 0);
}
long long pow(long long x, int n)//返回x的n次方
{
if (n == 0)
return 1;
if (n == 1)
return x;
if (isEven(n))
return pow(x * x, n / 2);
else
return pow(x * x, n / 2) * x;
}
int main()
{
cout << "2^61 = " << pow(2, 61) << endl;
cout << "2^62 = " << pow(2, 62) << endl;
}