算法设计与分析 绪论
《算法设计与分析》 Anany Levitin著,潘彦译 清华大学出版社
1.1 什么是算法
简答介绍了算法的概念,举出了2个例子:欧几里得算法和埃拉托色尼筛。
呵呵,这里说一下之前在校内上许多计算机专业的学生转载的有趣的一个段子:"早上去图书馆一女生背着一堆书进了阅览室,结果警报响了,大妈让女生看看是哪本书把警报弄响了,那女生把书倒出来,准备一本一本的测。大妈见状急了,把书分成两份,第一份过了一下,响了。又把这一份分成两份接着测,三回就找到了,大妈用鄙视的眼神看着女生,仿佛在说连O(n)和O(log2(n))都分不清"
1,最大公约数-----欧几里得算法
gcd(m,n) = gcd(n,m mode n)
递归和非递归的实现:
package Section1; /*绪论1.1 欧几里得算法:辗转相除求最大公约数*/ public class GCD { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(GCD(45,255)); System.out.println(gcd(255,45)); } public static int GCD(int a,int b) { //递归的形式 if(b == 0) return a; else return GCD(b,a % b);//总是将较小的数放前面 //return GCD(a % b,b);//错!! } public static int gcd(int a,int b){ //非递归的形式 if(b == 0) return a; int temp; while(b != 0) { //temp = b % a; temp = a % b;//比b小,将较小的数移到后面去当b a = b; b = temp; } return a; } }
2,埃拉托色尼筛素数筛选方法
要筛选2--n的素数,从2到根号n,筛选,将2到根号n的倍数删除,剩余的即是素数
package Section1; import java.util.Scanner; /*绪论1.1 埃拉托色尼素数筛选方法的实现*/ public class PrimeFilter { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Scanner scan = new Scanner(System.in); int n = scan.nextInt(); int[] a; while(n != 0) { a = primeFilter(n); for(int i = 0;i < a.length;i++) { if(a[i] == 0) break; else System.out.print(a[i] + " "); } n = scan.nextInt(); } } public static int[] primeFilter(int n){ //用数组返回整数n以内的所有的素数 int[] num = new int[n]; for(int i = 0;i < n;i++) //全部n以内的数放在num中 num[i] = i + 1; for(int i = 2;i * i <= n;i++) //开始筛选 for(int j = i;j < n;j++) if(num[j] != 0 && num[j] % i == 0) num[j] = 0; int[] result = new int[n]; //结果数组 int count = 0; for(int i = 1;i < n;i++) if(num[i] != 0) result[count++] = num[i]; return result; } }
习题1.1 11题
模拟门的状态即可:
package Section1; /*绪论1.1 习题11 带锁的门*/ public class LockedDoor { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int[] door = doorStatus(10); System.out.println("门的状态是:"); for(int i = 1;i < door.length;i++) System.out.print(door[i] + " "); } public static int[] doorStatus(int n){ //n个门经过n次后各个门的状态 int[] door = new int[n+1];//初始所有门都关着,0表示关,1表示开 for(int i = 1; i <= n;i++) //经过n次来改变门状态 { for(int j = i;j <= n;j++) if(j % i == 0)//要改变的门 { if(door[j] == 0) door[j] = 1; else door[j] = 0; } } return door; } }
1.2 算法问题求解基础
确定适当的数据结构,算法描述,正确性证明,算法效率分析,写伪代码
1.3 重要的问题类型
排序,查找;
字符串处理;
图论
排列组合
几何问题
数值
习题1.3 字符串匹配设计一个简单的算法
当然,最简单的就是蛮力匹配了,很简单就不写了。这里顺便学习和写了一下KMP算法:
书上,网上都有许多关于kmp算法的分析和论述,不再详述,只说它的核心思想,根据模式串自身的特征来提高匹配的效率,其关键在于求模式串的next函数
http://www.matrix67.com/blog/archives/115/ 一个网友的kmp分析
package Section1; /*绪论1.3 习题3 字符串匹配的kmp算法*/ public class KMP { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub String S = "ababcabcacb"; String pattern = "cabc"; System.out.println("模式串在S中匹配的位置为: " + indexOfMatch(S,pattern)); } public static int indexOfMatch(String S,String pattern){ //求模式串在S中匹配的下标(从0计) int index = -1; int[] next = next(pattern); //!!!模式串的下标i处与S匹配失败后,将从模式串的next[i]处开始与S匹配失败的那个地方比较 int j = 0 ,i = 0;//分别标记下一个要比较的位置 while(j < S.length() && i < pattern.length()) { if(pattern.charAt(i) == S.charAt(j)) { i++; j++; } else if(i == 0) //不匹配,而且是第一个位置就不匹配 j++; else //不匹配,且不是第一个位置 i = next[i]; } if(i == pattern.length()) return j-pattern.length(); //返回在S中第一个匹配的位置; else return index; } private static int[] next(String pattern){ //求模式串的next函数 int[] next = new int[pattern.length()]; int i = 0; while(i < next.length) //求每个位置的next值 { if(i == 0) next[i] = 0; else { for(int n = 0;n < i;n++) if(pattern.substring(0, n).equals(pattern.substring(i-n, i)) && pattern.charAt(n) != pattern.charAt(i)) next[i] = n; } i++; } return next; } }
毫无技巧处理的蛮力匹配时间复杂度是mn,而kmp可将时间效率提高到m+n。其实写一下kmp算法主要是了解一下这种处理字符串的思想和技巧,时间久了估计还是忘了,关键是这种思想要知道。
1.4 基本的数据结构
习题7,如何用下列数据结构实现ADT优先队列:
无序数组
有序数组
二叉查找树
实现方法略,说一点,数据结构的抽象!