数组求和-迭代
**时间复杂度一知半解,继续学习 **
int Sum(int A[],int n){
int sum=0; //O(1)
for(int i = 0;i < n; i++){ //O(n)
sum += A[i]; //O(1)
}
return sum; //O(1)
}
时间复杂度:O(n)
空间复杂度:计算除输入外,循环中需要的空间,在此包括累加器sum和内部循环的控制变量i,所以空间复杂度为O(2)
减而治之策略
思想:将n看作迭代的规模,循环每经过一次迭代,尚未参与统计的元素个数,即剩余的有效问题的规模就递减一个元素,这种不断蚕食,削弱有效问题规模的大小,称为减而治之策略。
数组求和-线性递归 / 递归跟踪
int Sum(int A[],int n){
return n < 1 ? 0 : Sum(A,n-1) + A[n-1];
//A[n-1]为平凡的问题,sum(A,n-1)为更小规模的问题
}
时间复杂度:递归实例sum(A,n-1)可以忽视掉,因为本身消耗为O(1),可以归纳到它所调用的递归实例中。所以计算其复杂度,即O(1) * (n + 1);也就是渐近的O(n);
每一个递归实例只会生成一个递归实例,可以组成线性次序,称为线性递归。简单直观,只适用于简单的递归模式。
“递归基”是递归函数的一种平凡情况,只有有递归基,递归才不会一直进行下去。
递归方程:
将规模为n的问题,划分为规模为n-1的缩减问题,与O(1)的平凡问题之和。
数组倒置-递归
void reserve( int * A ; int lo ; int hi){
if( lo < hi){
swap( A[lo] , A[hi] );
reserve(A; lo + 1; hi -1);
}
else {
}
}
思想:
-迭代原始版:
void reserve( int * A ; int lo ; int hi){
if( lo < hi){ //判断递归基
swap( A[lo] , A[hi] );
lo++;
hi--;
goto next;
}
else {
}
}
-迭代精简版:
while(lo < hi){
swap(A[lo ++] ;A[hi--]);
}
分而治之策略
数组求和-二分递归
int Sum(int A[],int lo; int hi){
if(lo = hi)
{
return A[lo];
}
int mid = (lo + hi) >> 1;
return Sum(A. lo, mid) + Sum(A, mid + 1; hi);
}
时间复杂度:T(n) = O(1)*n = O(n)
数组中最大的两个整数值,要求元素比较的次数尽可能少
双指针思想
指定两个指针存储最大值及次大值索引,迭代数组,先于次大值x2比较,再与最大值x1比较
比较次数:最好情况 1+(n-2)*1 = n-1
最坏情况 1+(n-2)*2 = 2n-3
public int[] max2(int A[],int lo,int hi)
{
int x1 = lo,x2 = lo +1; //初始化x1,x2;x1存储最大值,x2存储次大值
if(A[x1 = lo] < A[x2 = lo +1])
{
int i = x1;
x1 =x2 ;
x2 = i;
}
//从lo+2即第三个元素开始比较,先与x2次大值比较,在于最大值比较
for(int i=lo + 2;i < hi;i++)
{
if(A[i] > A[x2]) {
x2 = i;
if(A[i]>A[x1])
{
x1 = i;
}
}
}
int [] Res = {A[x1],A[x2]};
return A[x2];
}
递归的分而治之策略求两个最大值
将数组分为左右部分,分别求出最大值和次大值,再将两个最大值进行比较求出整体最大值,将次大值与较小最大值比较得出次大值。
动态规划
在效率上讲,递归并不总是能让我们满意。
动态规划:通过递归找到问题的本质,并给出初步的解之后,再将其等效的转化为迭代的形式。
Fib()函数
1、递归思想
当计算不超过100项时,计算时间就已经超过了三生三世,所以算法虽然在逻辑上没有问题,但是在计算时间上存在问题。
分析低效率原因:各递归实例被重复多次的调用
2、解决思路:令每一个递归实例只计算一次,先检查是否已经对此进行过计算,若计算过则直接取值,从而减少计算次数。
解决方法A(记忆):将已计算过结果的实例制表备查;空间复杂度为O(n)
解决方法B(动态规划):颠倒计算方向,由自顶而下递归改为自下而上迭代。
int g = 1, f = o; //f(1)和f(0)
while(o < n--)
{
g = g + f;
f = g - f;
}
return g;
复杂度分析:时间复杂度与n有关,为O(n);空间复杂度只占用了g和f两个存储空间,即两个常数复杂度O(1)
最长公共子序列LCS
两个序列公共子序列中的最长者
递归求解:
1、通过序列长度确定递归基;
2、减而治之策略:首先对末端字符比较,若相等,则去除末端,变成更小规模的子问题与平凡问题。
3、分而治之策略:若末端字符不相等,则分为两种情况,去除任一序列的末端字符与另一完整序列继续比较。
复杂度分析:每经过一次对比,原问题规模减一,即至少一个序列的长度减一。
最好的情况是只出现减而治之,则复杂度为O(n+m)
最坏情况如下,出现大量重复的子问题,复杂度为O(2n)
引入动态规划:消除重复计算,提高效率