数据结构与算法设计(读书笔记):2.算法分析
2016-04-03 20:50
274 查看
数学基础:
定义:
如果只是小量输入的情况,那么花费大量时间去努力设计聪明的算法恐怕并不值得。因此,好的算法应该是因地制宜的,不能盲目。
算法分析的基本策略是从内部(或最深层部分)向外展开的。
正常的用递归解法求解Fib的算法之所以缓慢,是因为有大量的多余的工作量,重复计算较多,可以通过保留一个简单的数组并使用一个for循环将运行时间降下来。
接下来,我们比较四种不同的求解最大子序列和问题的算法。
算法1。
时间复杂度为N的3次方。
算法2.
时间复杂度为N的2次方。
“分治”策略:
把问题分成两个大致相等的子问题,然后递归地对他们求解,这是分。
“治”是将两个子问题的解合到一起,并可能做些少量的附加工作,最后得到整个问题的解。
在我们的例子中,最大子序列只可能在3处出现。1.整个出现在输入数据的左半部2.整个出现在输入数据的右半部。针对这两种情况都可以用递归求解。3.最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起。
算法3.
时间复杂度为NlogN.都是用递归,Fib低效而本例却比较高效,是因为每次递归降低问题的维度不一样,前者维度下降慢,后者则是对半的下降。
算法4.
时间复杂度为N。
该算法的一个附带优点是,它只对数据进行一次扫描,一旦a[i]被读入并被处理,他就不再需要被记忆。因此,如果数组在磁盘上,他就可以被顺序读入,在主存中不必存储数组的任何部分。不仅如此,在任意时刻,算法都能对它已经读入的数据给出子序列问题的正确答案(其他算法不具有这种特性)。具有这种特性的算法叫做联机算法(on-line algorithm)。仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。
运行时间中的对数:
分析算法最混乱的方面集中在对数上,我们已经看到某些分治算法将以NlogN时间运行。除分治算法外,对数最常出现的规律概括为下列一般法则:
如果一个算法用常数时间将问题的大小削减为其一部分(通常是一半),那么该算法就是logN。
另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1),那么这种算法就是N的。
具有对数特点的三个例子:
1.对分查找(Binary Search),在数据稳定(即不允许插入操作和删除操作)的应用中,Binary Search非常有用。此时输入数据需要一次排序(前提),但是此后访问就很快了。
2.计算最大公因数的欧几里得算法。
3.幂运算
定义:
如果只是小量输入的情况,那么花费大量时间去努力设计聪明的算法恐怕并不值得。因此,好的算法应该是因地制宜的,不能盲目。
算法分析的基本策略是从内部(或最深层部分)向外展开的。
正常的用递归解法求解Fib的算法之所以缓慢,是因为有大量的多余的工作量,重复计算较多,可以通过保留一个简单的数组并使用一个for循环将运行时间降下来。
接下来,我们比较四种不同的求解最大子序列和问题的算法。
算法1。
public static int maxSubSum1(int[] a){ int maxSum = 0; for (int i = 0; i < a.length; i++) { for (int j = i; j < a.length; j++) { int thisSum = 0; for (int k = i; k<= j; k++) { thisSum+=a[k]; } if(thisSum>maxSum){ maxSum = thisSum; } } } return maxSum; } //该算法的思想比较直接。i从0到length,j从i到length,再用k(位于i与j之间)将i与j之间的sum计算出来。这样取最大的sum即为所求。
时间复杂度为N的3次方。
算法2.
public static int maxSubSum2(int[] a){ int maxSum = 0; for (int i = 0; i < a.length; i++) { int thisSum = 0; for (int j = i; j < a.length; j++) { thisSum+=a[j]; if(thisSum>maxSum){ maxSum = thisSum; } } } return maxSum; } //这里的算法2只是1的一个小改动,将原来的: for (int k = i; k<= j; k++) { thisSum+=a[k]; } 换成了: thisSum+=a[j];并将thisSum提出(显然的) //事实上也应该是这样。最大子序列和问题的本质,就是每个子序列的首与尾的位置不固定,那么我们只需要循环两次,每次得到确定首尾的sum值,然后取最大就可以了
时间复杂度为N的2次方。
“分治”策略:
把问题分成两个大致相等的子问题,然后递归地对他们求解,这是分。
“治”是将两个子问题的解合到一起,并可能做些少量的附加工作,最后得到整个问题的解。
在我们的例子中,最大子序列只可能在3处出现。1.整个出现在输入数据的左半部2.整个出现在输入数据的右半部。针对这两种情况都可以用递归求解。3.最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起。
算法3.
public static int maxSubSum3(int[] a,int left,int right){ //基本情况 if(left==right){ if(a[left]>0){ return a[left];//最大子序列 }else{ return 0;//不选取该值作为最大子序列的一部分,所以应该返回0 } } //分治 int center = (left+right)/2; int leftMaxSum = maxSubSum3(a, left, center); int rightMaxSum = maxSubSum3(a, center+1, right); int leftBorderSum = 0,leftBorderMaxSum=0; //固定一端center,达到前面的最大子序列 for (int i = center; i >= left; i--) { leftBorderSum+=a[i]; if(leftBorderSum>leftBorderMaxSum){ leftBorderMaxSum = leftBorderSum; } } int rightBorderSum = 0,rightBorderMaxSum=0; //固定一端center,达到后面的最大子序列 for (int i = center+1; i <= right; i++) { rightBorderSum+=a[i]; if(rightBorderSum>rightBorderMaxSum){ rightBorderMaxSum = rightBorderSum; } } int twoBorderMaxSum = leftBorderMaxSum+rightBorderMaxSum; int maxSum1 = Math.max(leftMaxSum,rightMaxSum); int maxSum = Math.max(maxSum1, twoBorderMaxSum); return maxSum; }
时间复杂度为NlogN.都是用递归,Fib低效而本例却比较高效,是因为每次递归降低问题的维度不一样,前者维度下降慢,后者则是对半的下降。
算法4.
public static int maxSubSum4(int[] a){ int maxSum =0,thisSum = 0; for (int i = 0; i < a.length; i++) { thisSum+=a[i]; if(thisSum>maxSum){ maxSum = thisSum; } if(thisSum<0){ thisSum = 0; } } return maxSum; } //分析原因,注意,像算法1和算法2一样,j代表当前序列的终点,而i代表当前序列的起点。碰巧的是,如果我们确实不需要知道最佳的子序列在哪里,那么i的使用可以脱离程序进行优化,我们要改进算法2。 //一个重要结论是,如果a[i]是负的,那么它不可能代表最优序列的起点。类似的,任何负的子序列不可能是最优子序列的前缀。
时间复杂度为N。
该算法的一个附带优点是,它只对数据进行一次扫描,一旦a[i]被读入并被处理,他就不再需要被记忆。因此,如果数组在磁盘上,他就可以被顺序读入,在主存中不必存储数组的任何部分。不仅如此,在任意时刻,算法都能对它已经读入的数据给出子序列问题的正确答案(其他算法不具有这种特性)。具有这种特性的算法叫做联机算法(on-line algorithm)。仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。
运行时间中的对数:
分析算法最混乱的方面集中在对数上,我们已经看到某些分治算法将以NlogN时间运行。除分治算法外,对数最常出现的规律概括为下列一般法则:
如果一个算法用常数时间将问题的大小削减为其一部分(通常是一半),那么该算法就是logN。
另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1),那么这种算法就是N的。
具有对数特点的三个例子:
1.对分查找(Binary Search),在数据稳定(即不允许插入操作和删除操作)的应用中,Binary Search非常有用。此时输入数据需要一次排序(前提),但是此后访问就很快了。
2.计算最大公因数的欧几里得算法。
public static long gcd(long M,long N){ while(N!=0){ long rem = M%N; M = N; N = rem; } return M; }
3.幂运算
相关文章推荐
- 只需十分钟,结果大不同 |《深度思考》
- 《洋葱阅读法》读书笔记
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- C#数据结构之顺序表(SeqList)实例详解
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 数据结构之Treap详解