以下答案纯属个人愚见,作为IT新手,算法代码中难免有逻辑漏洞和其他不足之处,欢迎朋友你点评拍砖,交流争辩能极大开阔思维,愿一起加油进步!^_^
1.1.19 在计算机上运行以下程序:
1 public class Fibonacci { 2 3 public static long F(int N) { 4 if(0 == N) 5 return 0; 6 if(1 == N) 7 return 1; 8 return F(N - 1) + F(N - 2); 9 } 10 11 public static void main(String[] args) { 12 for(int N = 0; N < 100; ++N) 13 StdOut.println(N + " " + F(N)); 14 } 15 16 }
计算机用这段程序在一个小时之内能够得到F(N) 结果的最大N 值是多少?开发F(N) 的一 个更好的实现,用数组保存已经计算过的值。
1 public class Fibonacci { 2 3 // Fibonacci数列计算,时间空间复杂度优化版 4 private static int M = 100; 5 private static long[] fib = new long[M]; 6 public static long fibonacciOptimization(int N) { 7 if(0 == N) 8 fib[0] = 0; 9 else if(1 == N) 10 fib[1] = 1; 11 else 12 fib[N] = fib[N - 1] + fib[N -2]; 13 return fib[N]; 14 } 15 16 public static void main(String[] args) { 17 for(int N = 0; N < 100; ++N) { 18 fib[N] = fibonacciOptimization(N); 19 StdOut.println(N + "\t" + fib[N]); 20 } 21 } 22 23 }
1.1.20 编写一个递归的静态方法计算 ln( N! ) 的值。
1 public static long factorial(int M) { 2 if(0 == M || 1 == M) 3 return 1; 4 else 5 return M * factorial(M - 1); 6 } 7 8 public static double ln(int N) { 9 return Math.log(factorial(N)); 10 } 11 12 public static void main(String[] args) { 13 double val = ln(4); 14 StdOut.println(val); 15 }
1.1.21 编写一段程序,从标准输入按行读取数据,其中每行都包含一个名字和两个整数。然后用printf() 打印一张表格,每行的若干列数据包括名字、两个整数和第一个整数除以第二个整数的结果,精确到小数点后三位。可以用这种程序将棒球球手的击球命中率或者学生的考试分数制成表格。
1 public static void main(String[] args) { 2 int M = 3; 3 int index = 0; 4 String[] strs = new String[M]; 5 while(index < M) 6 strs[index++] = StdIn.readLine(); 7 for(int i = 0; i < strs.length; ++i) { 8 String[] arr = strs[i].split("\\s+"); 9 double temp = Double.parseDouble(arr[1]) / Double.parseDouble(arr[2]); 10 StdOut.printf("%-10s %-10s %-10s %-13.3f\n", arr[0], arr[1], arr[2], temp); 11 }; 12 }
1.1.22 使用1.1.6.4 节中的rank() 递归方法重新实现BinarySearch 并跟踪该方法的调用。每当该方法被调用时,打印出它的参数lo 和hi 并按照递归的深度缩进。提示:为递归方法添加一个参数来保存递归的深度。
1 /** 2 * 递归查找关键词的索引 3 * @param key 4 * @param arr 5 * @param low 6 * @param high 7 * @return 8 */ 9 public static int rank(int key, int[] arr, int low, int high, int depth) { 10 printCallInfo(low, high, depth); 11 if(low > high) 12 return -1; 13 int mid = low + ((high - low) >> 1); 14 if(key < arr[mid]) 15 return rank(key, arr, low, mid - 1, depth + 1); 16 else if(key > arr[mid]) 17 return rank(key, arr, mid + 1, high, depth + 1); 18 else 19 return mid; 20 } 21 22 /** 23 * 二分查找 : 递归描述 24 * @param key 25 * @param arr 26 * @return 27 */ 28 public static int binarySearch(int key, int[] arr, int depth) { 29 return rank(key, arr, 0, arr.length - 1, depth); 30 } 31 32 /** 33 * 打印缩进 34 * @param indents 缩进数 35 */ 36 private static void printIndent(final int indents) { 37 for(int i = 0; i < indents; ++i) 38 StdOut.print("----------"); 39 } 40 41 /** 42 * 打印调用信息 43 * @param low 44 * @param high 45 * @param depth 46 */ 47 private static void printCallInfo(int low, int high, int depth) { 48 StdOut.print(depth + "\t"); 49 printIndent(depth); 50 StdOut.println(low + "\t" + high); 51 } 52 53 public static void main(String[] args) { 54 int N = 1024; 55 int[] arr = new int[N]; 56 for(int i = 0; i < N; ++i) 57 arr[i] = StdRandom.uniform(N * 50); 58 // 排序 59 Arrays.sort(arr); 60 // 从随机数组中随机抽取一个元素作为关键字 61 // 输出随机数组 62 StdOut.print("seq = "); 63 for(int i = 0 ; i < N; ++i) 64 StdOut.print(arr[i] + "\t"); 65 int key = arr[StdRandom.uniform(N)]; 66 StdOut.println("\nkey = " + key); 67 StdOut.println("---------------------------------------------------------------------------------------"); 68 binarySearch(key, arr, 0); 69 }
1.1.27 二项分布。估计用以下代码计算binomial(100, 50) 将会产生的递归调用次数:
1 public static double binomial(int N,int k, double p) { 2 if(N == 0 && k == 0) 3 return 1.0; 4 if(N < 0 || k < 0) 5 return 0.0; 6 return (1.0 - p) * binomial(N-1, k, p) + p * binomial(N-1, k-1, p); 7 }
将已经计算过的值保存在数组中并给出一个更好的实现。
1 private static int binom_N = 100; 2 3 private static int binom_k = 50; 4 5 private static double[][] binom = new double[binom_N + 1][binom_k + 1]; 6 7 private static double binomial(int N, int k, double p) { 8 if(N < 0 || k < 0) { 9 return 0.0; 10 } else if(N == 0 && k == 0) { 11 if(binom[N][k] == -1.0) 12 binom[N][k] = 1.0; 13 } else { 14 if (binom[N][k] == -1.0) 15 binom[N][k] = (1.0 - p) * binomial(N - 1, k, p) + p * binomial(N - 1, k - 1, p); 16 } 17 return binom[N][k]; 18 } 19 20 public static void main(String[] args) { 21 // 数组binom初始化 22 for(int i = 0; i < binom_N + 1; ++i) 23 for(int j = 0; j < binom_k + 1; ++j) 24 binom[i][j] = -1.0; 25 // 计算概率 26 double res = binomial(binom_N, binom_k, 0.25); 27 StdOut.println(res); 28 }
1.1.28 删除重复元素。修改BinarySearch 类中的测试用例来删去排序之后白名单中的所有重复元素。
1.1.29 等值键。为BinarySearch 类添加一个静态方法rank(),它接受一个键和一个整型有序数组(可能存在重复键)作为参数并返回数组中小于该键的元素数量,以及一个类似的方法count() 来返回数组中等于该键的元素的数量。注意:如果i 和j 分别是rank(key,a) 和count(key,a)的返回值,那么a[i..i+j-1] 就是数组中所有和key 相等的元素。看法:只有当key在数组中时才这样子。
1 /** 2 * 二分查找统计 3 * @param key 待查找关键字 4 * @param arr 待查找数组 5 * @return 返回小于key的个数即比等于key的第一个元素的索引值,若找不到则返回-1 6 */ 7 private static int countLowers(int key, int[] arr) { 8 int low = 0; 9 int high = arr.length - 1; 10 while(low <= high) { 11 int mid = low + ((high - low) >> 1); 12 if(key < arr[mid]) 13 high = mid - 1; 14 else if(key > arr[mid]) 15 low = mid + 1; 16 else { 17 while(mid > 0 && arr[mid] == arr[mid - 1]) // 注意判断条件的先后顺序 18 -- mid; 19 return mid; 20 } 21 } 22 return low; // -1; 根据算法原理可知low是小于key的个数 23 } 24 25 /** 26 * 统计与key相等的个数 27 * @param key 待查找关键字 28 * @param arr 待查找数组 29 * @return 返回与key相等的个数 30 */ 31 private static int countEquals(int key, int[] arr) { 32 int lowers = countLowers(key, arr); 33 int idx = lowers; 34 if(idx == arr.length || key != arr[idx]) // 注意判断条件的先后顺序 35 return 0; 36 37 int cnt = 1; 38 while((idx < arr.length - 1) && (arr[idx] == arr[idx + 1])) { // 注意判断条件的先后顺序 39 ++ cnt; 40 ++ idx; 41 } 42 return cnt; 43 } 44 45 public static void main(String[] args) { 46 // 从文件读取数据 47 In in = new In("./data/tinyW.txt"); 48 int[] whiteList = in.readAllInts(); 49 // 排序并打印输出 50 Arrays.sort(whiteList); 51 for(int idx = 0; idx < whiteList.length; ++idx) 52 StdOut.print(whiteList[idx] + "\t"); 53 StdOut.println(); 54 // 从控制台读取关键字 55 int key = StdIn.readInt(); 56 int lowers = countLowers(key, whiteList); 57 StdOut.println("小于\t" + key + "\t的个数是:\t" + lowers); 58 int equals = countEquals(key, whiteList); 59 StdOut.println("等于\t" + key + "\t的个数是:\t" + equals); 60 }
1.1.30 数组练习。编写一段程序,创建一个N×N 的布尔数组a[][]。其中当i 和j 互质时(没有相同因子),a[i][j] 为true,否则为false。
1 /** 2 * 判断两数是否互质,若两数的最大公约数是1则两数互质 3 * @param a 4 * @param b 5 * @return 若互质则true,否则false 6 */ 7 private static boolean isCoprime(int a, int b) { 8 for(int i = 2; i < Math.sqrt(a); ++i) { 9 if(a % i == 0 && b % i == 0) 10 return false; 11 } 12 return true; 13 } 14 15 /** 16 * 使用2300多年前的欧几里得算法求解两数的最大公约数 17 * @param p 数一 18 * @param q 数二 19 * @return 最大公约数 20 */ 21 private static int gcd(int p, int q) { 22 if(q == 0) 23 return p; 24 int r = p % q; 25 return gcd(q, r); 26 } 27 28 private static boolean[][] boolArray(int N) { 29 // 创建NxN的布尔二维数组 30 boolean[][] boolArr = new boolean[N][N]; 31 for(int i = 1; i <= N; ++i) 32 for(int j = 1; j <= N; ++j) 33 if(1 == gcd(i, j)) 34 boolArr[i - 1][j - 1] = true; 35 else 36 boolArr[i - 1][j - 1] = false; 37 return boolArr; 38 } 39 40 public static void main(String[] args) { 41 int N = 5; 42 boolean[][] boolArr = boolArray(N); 43 for(int i = 0; i < N; ++i) { 44 for (int j = 0; j < N; ++j) 45 StdOut.print(boolArr[i][j] + "\t"); 46 StdOut.println(); 47 } 48 }
1.1.31 随机连接。编写一段程序,从命令行接受一个整数N 和double 值p(0 到1 之间)作为参数,在一个圆上画出大小为0.05 且间距相等的N 个点,然后将每对点按照概率p 用灰线连接。
1 /** 2 * 画圆 3 * @param x 圆心x坐标 4 * @param y 圆心y坐标 5 * @param r 半径r 6 */ 7 private static void drawCircle(double x, double y, double r) { 8 StdDraw.setXscale(0, 2 * x); 9 StdDraw.setYscale(0, 2 * y); 10 StdDraw.setPenRadius(0.003); 11 StdDraw.setPenColor(StdDraw.BOOK_LIGHT_BLUE); 12 StdDraw.circle(x, y, r); 13 } 14 15 /** 16 * 在圆上描点 17 * @param x0 圆心x坐标 18 * @param y0 圆心y坐标 19 * @param r 半径r 20 * @param N N个点 21 */ 22 private static double[][] drawPoints(double x0, double y0, double r, int N) { 23 double[][] points = new double[N][2]; 24 StdDraw.setPenRadius(0.005); 25 StdDraw.setPenColor(StdDraw.BOOK_RED); 26 for(int idx = 0; idx < N; ++idx) { 27 double x = x0 + r * Math.cos(2 * Math.PI * idx / N); 28 double y = y0 + r * Math.sin(2 * Math.PI * idx / N); 29 StdDraw.point(x, y); 30 points[idx][0] = x; 31 points[idx][1] = y; 32 } 33 return points; 34 } 35 36 /** 37 * 以概率p随机连接顶点集points中的点 38 * @param points 点集 39 * @param p 概率p 40 */ 41 private static void randomLinkPoints(double[][] points, double p) { 42 StdDraw.setPenRadius(0.002); 43 StdDraw.setPenColor(StdDraw.LIGHT_GRAY); 44 int length = points.length; 45 for(int i = 0; i < length; ++i) 46 for(int j = 0; j < length; ++j) 47 if(true == StdRandom.bernoulli(p)) 48 StdDraw.line(points[i][0], points[i][1], points[j][0], points[j][1]); // 应该再建立一个包含x坐标和y坐标的数据结构 49 } 50 51 /** 52 * 在圆上画N个点然后每两点间以概率p连接 53 * @param N N个点 54 * @param p 概率p 55 */ 56 private static void randomLink(int N, double p) { 57 double x = 10.0; 58 double y = 10.0; 59 double r = 9.0; 60 drawCircle(x, y, r); 61 double[][] points = drawPoints(x, y, r, N); 62 randomLinkPoints(points, p); 63 } 64 65 public static void main(String[] args) { 66 randomLink(20, 0.2); 67 }
^_^