『算法』读书笔记 1.1Java语法 - 练习代码总结
Algorithms Fourth Edition
Chapter 1
本章结构
1.1Java语法 练习部分
1.2数据抽象
1.3集合类抽象数据类型:背包 (Bags) 、队列 (Queues) 、栈 (Stacks)
1.4面向操作的抽象数据类型
1.5连通性问题-Case Study: Union - Find ADT
ex1.1.3 从命令行得到三个整数参数。如果他们都相等则打印equal,否则打印not equal
public class PrintThreeInt { public static void main(String[] args) { int N1 = Integer.parseInt(args[0]); int N2 = Integer.parseInt(args[1]); int N3 = Integer.parseInt(args[2]); String Notification = "equal"; if (N1 != N2) Notification = "not equal"; if (N2 != N3) Notification = "not equal"; System.out.println(Notification); } }
总结:注意不能用return来返回字符串;且声明为void的方法不能有返回值
ex1.1.5 如果double类型的变量x和y都严格位于0和1之间则打印true,否则打印false
public class IsDoubleInPeriod { public static void main(String args[]) { double x = Double.parseDouble(args[0]); double y = Double.parseDouble(args[1]); String Notification = "false"; if ( 0 <= x && x <= 1 && 0 <= y && y <=1) Notification = "true"; StdOut.println(Notification); } }
总结:注意区间的表达,[0,1]应为0 <= x && x <= 1而不是0 〈= x <= 1
ex1.1.11 打印一个二维布尔数组的内容,其中使用*表示真,空格表示假。打印出行号和列号
public class PrintArray { public static void main(String args[]) { boolean[][] a = {{true,false},{true,true,false}}; for (int i = 0; i < a.length; i++) for (int j = 0; j < a[i].length; j++) { if (a[i][j] == true) System.out.println("a["+i+"]["+j+"]:"+"*"); else System.out.println("a["+i+"]["+j+"]:"+" "); } }
总结:在二维数组中,读取每一行的长度可用a[i].length,然后再进行遍历
ex1.1.13 打印一个M行N列的二维数组的转置
public class ConvertArray { public static void main(String args[]) { int[][] a = {{1,3},{2,2},{5,6}}; int[][] result_a = new int[2][3]; for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) { result_a[i][j] = a[j][i]; System.out.println("a["+i+"]["+j+"]:"+result_a[i][j]); } } }
问题:如何实现交错数组的转置?
ex1.1.14 编写一个静态方法lg(),接受一个整型参数N,返回不大于log2N的最大整数。不使用Math库
public class MathLg { private static int lg(int N) { int product = 1; int x = -1; if (N < 1) throw new RuntimeException("Error, N should be larger than 0"); else while (product <= N) //*,把等于的情况也纳入进来,从而避免了在如23>5这种情况的自增导致输出结果为3的情况 { product *= 2; x++; } return x; } public static void main(String args[]) { int N = Integer.parseInt(args[0]); StdOut.print(lg(N)); } }
注意:计数器x从-1开始计数,同时需要考虑计数器在如23>5这种情况的自增,见*
ex1.1.15 编写一个静态方法histogram(),接受一个整型数组a[]和一个整数M为参数并返回一个大小为M的数组,其中第i个元素为整数i在参数数组中出现的次数。如果a[]中的值均在0到M-1之间,返回数组中的所有元素之和应该和a.length相等
public class Histogram { private static void histogram(int[] a, int M) { int[] b = new int[M]; if (M > a.length) System.out.println("We don't have enough data in the input array"); //int count =0; for (int i = 0; i < M; i++) { int count =0; for (int j = 0; j < a.length; j++) { //if (a[j] < M) //{ if (i == a[j]) count++; //} } b[i] = count; } for (int i = 0; i < b.length; i++) StdOut.println("Value of element " + i + " is " + b[i]); } public static void main(String[] args) { int[] a = {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,6,7,8,9}; int M = 5; histogram(a, M); } }
总结:
* 1.尽量使用面向对象的思维:一开始把打印数组部分放在主方法中,后来发现直接在histogram()实现更佳,同时也便于调试
* 2.覆盖各种情况是本题之关键:
a.考虑M的值大于输入数组长度的情况;
b.考虑M小于数组中某些元素的值时,如何处理这些数字,例如本例中可直接忽略不处理
* 3.计数器应该放在第一个循环中,这样才能在完成一次查找时(子循环)清空计数器
* 4.数组打印必须采用遍历的形式,直接println()得到的只是数组的索引
ex1.1.19 题中给了一个利用递归实现菲波纳契数列,要求开发一个更好的实现,用数组保存已经计算过的值
public class Fibonacci{ public static long F(int N) { if (N == 0) return 0; if (N == 1) return 1; long[] f = new long[N+1];//'Cause we need to deal with f[N] and f[0], N+1 elements are necessary f[0] = 0; f[1] = 1; for (int i = 2; i <= N; i++)//i must calculate to N so that f[N] can be reached { f[i] = f[i-1] + f[i-2]; } return f[N]; } public static void main(String[] args) { for (int N = 0; N < 100; N++) StdOut.println(N + " " + F(N)); } }
总结:经过改进后的算法效率提升明显。注意:数列增长速度很快,int类型以及不能很好的满足需求,宜采用long或大数据类型
ex1.1.20 编写一个递归的静态方法计算ln(N!)的值
public class Calln { private static double ln(int N) { int product = 1; if (N == 0) return 0; if (N == 1) return 1; for (int i = 2; i <= N; i++) product *= i ; return Math.log(product); //Don't know whether ln(N!) can be solved in this way. } public static void main(String[] args) { int N = Integer.parseInt(args[0]); double value = ln(N); StdOut.print(value); } }
问题:在13行利用了Math.log函数库进行对数运算,如果不使用这个库应该如何处理?
ex1.1.21 从标准输入按行读取数据,其中每行都包含一个名字和两个整数。然后用printf()打印一张表格,每行的若干列数据包括名字、两个整数和第一个整数除以第二个整数的结果,精确到小数点后三位。
public class StudentGrade { public static void main(String[] args) { //Read all data from Standard Input and store them in String s String[] s = StdIn.readStrings(); //Deal with data by person int N = s.length / 3; for (int i = 0; i < N; i++) { String name = s[3*i]; int mathGrade = Integer.parseInt(s[3*i+1]); int averageGrade = Integer.parseInt(s[3*i+2]); double percentage = (double)mathGrade / (double)averageGrade; //Pay attention to the conversion StdOut.printf("%s" + " | " + "%d" + " | " + "%d" + " | " + "%.3f\n", name, mathGrade, averageGrade, percentage); } } }
总结:
* 1.readStrings()的读取结果为字符串数组,凡是遇到空格或换行时自动创建新数组
* 2. 异常 ArrayIndexOutOfBoundsException Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
* 3.整型除以整型结果仍为整型,即便是将结果赋值给double。必须先强制转换需要计算的整数。
ex1.1.24 从命令行接受两个参数,计算它们的最大公约数并打印出每次调用递归方法时的两个参数。
public class Euclid { private static int gcd(int p, int q) { System.out.println("p: " + p + ", q:" + q); if (q == 0) return p; int r = p % q; return gcd(q, r); } public static void main(String[] args) { int N1 = Integer.parseInt(args[0]); int N2 = Integer.parseInt(args[1]); int p = gcd(N1, N2); System.out.println("The largest common divisor is " + p); } }
ex1.1.23 为BinarySearch的测试用例添加一个参数:+打印出标准输入中不在白名单上的值;—则打印出标准输入中在白名单上的值
import java.util.Arrays; public class BinarySearchNew { public static int rank(int key, int[] a) { int lo = 0; int hi = a.length -1; while (lo <= hi) { int mid = lo + (hi - lo) /2; if (key < a[mid]) hi = mid - 1; else if (key > a[mid]) lo = mid + 1; else return mid; } return -1; } public static void main(String[] args) { int[] whitelist = In.readInts(args[0]); String para = args[1]; //new parameter for determining whether to print values in the whitelist or not in the whihtlist Arrays.sort(whitelist); //ATTENTION: selective clause cannot be placed inside while loop //if the parameter is set as "+" if (para.equals("+")) while (!StdIn.isEmpty()) { int key = StdIn.readInt(); if (rank(key, whitelist) < 0) StdOut.println(key); } //if the parameter is set as "-" else if (para.equals("-")) while (!StdIn.isEmpty()) { int key = StdIn.readInt(); if (rank(key, whitelist) > 0) StdOut.println(key); } } }
总结:
* 1.注意字符串的比较,不能直接使用 ==,因为此时比较的是两字符串的引用。应当采用String1.equals(String2)的形式比较两字符串的值
* 2.In.readInts(args[0])这种情况指的是标准输入从文件中读取数据
问题:readInts()方法是如何从文件中读取数据的?为什么一长串连续的数字会被分成两个数字为一组的数组,看相关的Javadoc发现它利用了字符串的split来分割数字,可是存储数字的文件中并没有出现任何制表符、空行、换行符或回车,不解
public static double[] readDoubles(String filename) { In in = new In(filename); String[] fields = in.readAll().trim().split("\\s+"); double[] vals = new double[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Double.parseDouble(fields[i]); return vals; }
读取的数据文件内容类似:23287974317892409871267891234