算法Sedgewick第四版-第1章基础-001递归
一、
方法可以调用自己(如果你对递归概念感到奇怪,请完成练习 1.1.16 到练习 1.1.22)。例如,
下面给出了 BinarySearch 的 rank() 方法的另一种实现。我们会经常使用递归,因为递归代码比
相应的非递归代码更加简洁优雅、易懂。下面这种实现中的注释就言简意赅地说明了代码的作用。
我们可以用数学归纳法证明这段注释所解释的算法的正确性。我们会在 3.1 节中展开这个话题并为
二分查找提供一个这样的证明。
编写递归代码时最重要的有以下三点。
递归总有一个最简单的情况——方法的第一条语句总是一个包含 return 的条件语句。
递归调用总是去尝试解决一个规模更小的子问题,这样递归才能收敛到最简单的情况。在下
面的代码中,第四个参数和第三个参数的差值一直在缩小。
递归调用的父问题和尝试解决的子问题之间不应该有交集。在下面的代码中,两个子问题各
自操作的数组部分是不同的。
1 public static int rank(int key, int[] a, int lo, int hi) { 2 //如果key存在于a[]中,它的索引不会小于lo且不会大于hi 3 if (lo > hi) return -1; 4 int mid = lo + (hi - lo) / 2; 5 if (key < a[mid]) return rank(key, a, lo, mid - 1); 6 else if (key > a[mid]) return rank(key, a, mid + 1, hi); 7 else return mid; 8 }
非递归算法
1 /** 2 * Returns the index of the specified key in the specified array. 3 * 4 * @param a the array of integers, must be sorted in ascending order 5 * @param key the search key 6 * @return index of key in array <tt>a</tt> if present; <tt>-1</tt> otherwise 7 */ 8 public static int indexOf(int[] a, int key) { 9 // 数组必须是有序的 10 int lo = 0; 11 int hi = a.length - 1; 12 while (lo <= hi) { 13 // Key is in a[lo..hi] or not present. 14 int mid = lo + (hi - lo) / 2; 15 if (key < a[mid]) hi = mid - 1; 16 else if (key > a[mid]) lo = mid + 1; 17 else return mid; 18 } 19 return -1; 20 }
违背其中任意一条都可能得到错误的结果或是低效的代码(见练习 1.1.19 和练习 1.1.27),而
坚持这些原则能写出清晰、正确且容易评估性能的程序。使用递归的另一个原因是我们可以使用数
学模型的来估计程序的性能。我们会在 3.2 节的二分查找以及其他几个地方分析这个问题。
You can do anything you set your mind to, man!