计算机只是我们的工具(手段),我们研究的对象是计算。
计算 = 信息处理
借助某种工具,遵照一定规则,以明确而机械的形式进行。
机器模型 = 计算机 = 信息处理工具
所谓算法,即特定计算模型下,旨在解决特定问题的指令序列
好算法的定义
程序 = 数据结构 + 算法
要改进或者优化某个算法,我们需要先知道这个算法的效率。
算法分析
通常我们对一个算法的度量值的大小都取这个度量规模的最坏情况。
对于特定问题+不同算法,我们该如何判断其优劣呢?考察以下两种模型
我们度量的单位为CPU的执行次数,算法在必要的时候需要复位。
n为问题规模。
常系数可忽略:
其他记号:
O的等级:
算法效率从O(2^n)到O(n^c)是一个难点。通常情况下,我们想找出一个效率从指数到多项式的算法是非常难的。这是一个分水岭。
定理:2-Subset is NP-complete。解释:就目前的计算模型而言,不存在可在多项式时间内回答此问题的算法。
NP问题就是一个幂集问题,效率为O(2^n)。
两个主要任务 = 正确性( 不变性*单调性 ) + 复杂度。
C++等高级语言的基本指令,均等效于RAM的基本指令;在渐进意义下,二者大体相当。
复杂度分析的主要方法:
迭代乃人工,递归方神通。
从效率上讲,迭代效率比递归效率要来的高。我们需要学习的是从递归到迭代的一个过程。
凡治众如治寡,分数是也。
对于一个问题,分成一系列子问题。通过求解子问题进而得出这个问题的解。
减而治之与分而治之。
第一个例子:斐波那契数列
递归版本的fib和迭代版的fib的效率相差巨大(原因重新计算已经被计算过的fib数)。
//递归版
int fib(int n){
return (2>n)? n : fib(n-1) + fib(n-2);
}
很明显这是求解一个幂集问题,这段代码时间复杂度是2的n次方,空间复杂度是O(n)。
改进1:记忆版本(制表)
//迭代
int arr[] = new int[n+1];
arr[1] = 1;
arr[2] = 1;
for(int i=3;i1]+arr[i-2];
}
return arr[n];
以上时间复杂度和空间复杂度都是O(n)。
改进2:动态规划(自底而上)
//迭代
int f=0,g=1;
while(0return g;
时间复杂度是O(n),空间复杂度是O(1)。
第二个例子:LCS序列
递归版(算法步骤):
对于序列 A[0, n] 和 B[0, m] 无非三种情况
public static String LCS(char A[],int alo,int ahi,char B[],int blo,int bhi){
//第一种情况
if(ahi==-1||bhi==-1){
return "";
}
//第二种情况
if(A[ahi]==B[bhi]){
return LCS(A,alo,ahi-1,B,blo,bhi-1)+A[ahi];
}
//第三种情况,由于这种情况将问题分成2个子问题。
else{
String AA = LCS(A,alo,ahi-1,B,blo,bhi);
String BB = LCS(A,alo,ahi,B,blo,bhi-1);
return AA.length()>BB.length()? AA : BB;
}
}
明显是一个幂集问题,所以时间复杂度为O(2^n),空间复杂度为O(n)。
由递归分析,我们得出一个结论,重复元素多次计算,所以我们可以改进。
迭代版(制表):
public static void main(String[] args) {
//测试数据
String A = "DATA";
String B = "NAA";
int n = A.length();
int m = B.length();
//start LCS
//初始化
int table[][] = new int[n+1][m+1];
for(int i=0;i0] = 0;
}
for(int j=0;j0][j] = 0;
}
//迭代
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//第二种情况,由于字符串是从0开始,而表格是从1开始,所以对于的table和字符串相差1
if(A.charAt(i-1)==B.charAt(j-1)){
table[i][j] = table[i-1][j-1]+1;
}
//第三种情况
else{
table[i][j] = table[i-1][j]>table[i][j-1] ? table[i-1][j] :table[i][j-1];
}
}
}
//end LCS
//测试结果
int count = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(table[i][j]==count){
System.out.print(A.charAt(i-1));
count++;
}
}
}
System.out.println();
}
显然时间复杂度和空间复杂度都为O(n*m)。