数据结构复习笔记(Data Structures & Algorithms in Java, 4th) - recursive 递归
2016-12-02 23:20
197 查看
重覆执行除了可以使用回圈(loop)来达到以外,另一个方法就是递归(recursive),面对递归问题时,一般的思路是:
首先思考一下子问题是什么,有多少子问题需要解决以得到 f(n)。也就是说,在一个 recursive binary tree 问题中,所有任一部份都依赖於二组子问题;而在链表的递归问题中,只需要解决一组子问题。总而言之,若要求得 f(n) 的解,则我们先计算 f(0) 或 f(1),通常也是一般固定的值。我们下面直接用一些简单的例子来解说递归:
1. 阶乘
比如说 4! = 4 * 3 * 2 * 1,求 n! = ?
我们可以规纳一下:
base case: 0! = 1
n! = n * (n-1) * (n-2) *.....*3*2*1
我们总结后 => f(n) = n * f(n-1)
2. 斐波那契数列: 1, 1, 2, 3, 5, 8, 13, 21, 34....
我们归纳一下:
Base case: f(0) = 0, f(1) = 1, f(2) = f(1) + f(0)
所以我们可以总结得到 => f(n) = f(n-1) + f(n-2)
public static <E> void reverseArray(E[] array, final int left, final int right) {
if (left < right) {
// swap objects at index i and index j
E temp = array[right];
array[right] = array[left];
array[left] = temp;
// recursive
reverseArray(array, left + 1, right - 1);
}
}Linear Recursion:最简单的递归是线性递归,也就是说在每次执行中,最多只执行一次逓归方法。比如说上面的例1
Binary Recursion:而当每次执行二次递归方法时,比如说上面的例2我们称其为 Binary Recursion。递归的方式可以让我们的代码写起来更简洁,而代价就是运行时多占用内存。在内存有限的情况下,我们就使用
while/ for loop 的方式解决问题。
练习
1. 写一段代码可以将返回集合内的所有子集
首先我们得想清楚我们的时间和空间复雜度。我们可以先这么想,每一个子集可以是存在或是不存在,所以每一个子集有2个选择,第一个有2个,第二个有2个…到最后一个有2个,因此,这一共有 2^n 个子集合。
解法一、递归
a. f(n) 集合数组 S = [0,1,...n], 第一个子集为S[0],则较小的集合数组 f(n-1) = S[1,2...n]。
b. 取得较小集合的所有子集合,还有第一项。遍历所有子集合,全加上第一项。
c. 将新集合加入。
字符串长度为0时,便是空字符串;若为1的话,则只有1种;如果是2的话,比如 "ab",就是 ab或是 ba;如果是 abc,就是 a 插入[bc]中的所有字符串组合, a插入[cb]中的所有字符串组合…依此类推 ...
3. 实现一个方法返回 n对括号的组合
范例:
输入:3 (e.g., 3 pairs of parentheses)
输出: ()()(), ()(()), (())(), ((()))
我们使用递归的方法,产生所有括号组合,然后打印出合格的。
left:只要我们还有左括号,我们可以不断产生左括号。
right:只要次数不多於左括号,我们便能产生右括号。
4. 小画家里,有个功能,就是将特定色块的颜色变成另外一种颜色。因此,我们用二维数组代表屏幕,开始变色的起始点,新旧颜色。根据已给出的条件,请实现该方法。
所有代码都在Github上
首先思考一下子问题是什么,有多少子问题需要解决以得到 f(n)。也就是说,在一个 recursive binary tree 问题中,所有任一部份都依赖於二组子问题;而在链表的递归问题中,只需要解决一组子问题。总而言之,若要求得 f(n) 的解,则我们先计算 f(0) 或 f(1),通常也是一般固定的值。我们下面直接用一些简单的例子来解说递归:
1. 阶乘
比如说 4! = 4 * 3 * 2 * 1,求 n! = ?
我们可以规纳一下:
base case: 0! = 1
n! = n * (n-1) * (n-2) *.....*3*2*1
我们总结后 => f(n) = n * f(n-1)
public static int getFactorial(int n) { if (n == 0) return 1; return n * getFactorial(n - 1); }
2. 斐波那契数列: 1, 1, 2, 3, 5, 8, 13, 21, 34....
我们归纳一下:
Base case: f(0) = 0, f(1) = 1, f(2) = f(1) + f(0)
所以我们可以总结得到 => f(n) = f(n-1) + f(n-2)
public static int getNthFibnacci(int n) { if(n == 0) { return 0; } else if(n == 1) { return 1; } else { return getNthFibnacci(n-1) + getNthFibnacci(n-2); } }3. 将数组逆向排列
public static <E> void reverseArray(E[] array, final int left, final int right) {
if (left < right) {
// swap objects at index i and index j
E temp = array[right];
array[right] = array[left];
array[left] = temp;
// recursive
reverseArray(array, left + 1, right - 1);
}
}Linear Recursion:最简单的递归是线性递归,也就是说在每次执行中,最多只执行一次逓归方法。比如说上面的例1
Binary Recursion:而当每次执行二次递归方法时,比如说上面的例2我们称其为 Binary Recursion。递归的方式可以让我们的代码写起来更简洁,而代价就是运行时多占用内存。在内存有限的情况下,我们就使用
while/ for loop 的方式解决问题。
练习
1. 写一段代码可以将返回集合内的所有子集
首先我们得想清楚我们的时间和空间复雜度。我们可以先这么想,每一个子集可以是存在或是不存在,所以每一个子集有2个选择,第一个有2个,第二个有2个…到最后一个有2个,因此,这一共有 2^n 个子集合。
解法一、递归
a. f(n) 集合数组 S = [0,1,...n], 第一个子集为S[0],则较小的集合数组 f(n-1) = S[1,2...n]。
b. 取得较小集合的所有子集合,还有第一项。遍历所有子集合,全加上第一项。
c. 将新集合加入。
public static <E> ArrayList<ArrayList<E>> getAllSubSet(ArrayList<E> set, int index) { ArrayList<ArrayList<E>> allSubSet; if (set.size() == index) { allSubSet = new ArrayList<>(); allSubSet.add(new ArrayList<E>()); // 空集合为任何集合的子集合 } else { allSubSet = getAllSubSet(set, index + 1); E item = set.get(index); ArrayList<ArrayList<E>> moreSubSet = new ArrayList<>(); // try to avoid concurrentMod exception for (ArrayList<E> subset : allSubSet) { ArrayList<E> newSubset = new ArrayList<E>(); newSubset.addAll(subset); newSubset.add(item); moreSubSet.add(newSubset); } allSubSet.addAll(moreSubSet); } return allSubSet; }2. 实现一个方法取得一个字符串的所有排列。
字符串长度为0时,便是空字符串;若为1的话,则只有1种;如果是2的话,比如 "ab",就是 ab或是 ba;如果是 abc,就是 a 插入[bc]中的所有字符串组合, a插入[cb]中的所有字符串组合…依此类推 ...
public static ArrayList<String> getAllPermutation(String text) { if (text == null) { return null; } ArrayList<String> permutations = new ArrayList<>(); if (text.isEmpty()) { permutations.add(""); return permutations; } final char first = text.charAt(0); String substring = text.substring(1); ArrayList<String> subPermStrings = getAllPermutation(substring); for (String perm : subPermStrings) { final int size = perm.length(); for (int i = 0; i <= size; i++) { permutations.add(addCharToStringAt(perm, i, first)); } } return permutations; }
private static String addCharToStringAt(String perm, int i, char first) { String start = perm.substring(0, i); String end = perm.substring(i); return start + first + end; }
3. 实现一个方法返回 n对括号的组合
范例:
输入:3 (e.g., 3 pairs of parentheses)
输出: ()()(), ()(()), (())(), ((()))
我们使用递归的方法,产生所有括号组合,然后打印出合格的。
left:只要我们还有左括号,我们可以不断产生左括号。
right:只要次数不多於左括号,我们便能产生右括号。
public static void getCharArray(int left, int right, char[] str, int strIndex) { if (left < 0 || right < left) { return; } if (left == 0 && right == 0) { System.out.println(str); } else { if (left > 0) { str[strIndex] = '('; getCharArray(left - 1, right, str, strIndex + 1); } if (right > left) { str[strIndex] = ')'; getCharArray(left, right - 1, str, strIndex + 1); } } }
4. 小画家里,有个功能,就是将特定色块的颜色变成另外一种颜色。因此,我们用二维数组代表屏幕,开始变色的起始点,新旧颜色。根据已给出的条件,请实现该方法。
public void paintFill(int[][] screen, int x, int y, int originColor, int newColor) { if (screen.length < y || screen[0].length < x || x < 0 || y < 0) { return; } if (screen[y][x] == originColor) { screen[y][x] = newColor; paintFill(screen, x, y - 1, originColor, newColor); // up paintFill(screen, x, y + 1, originColor, newColor); // down paintFill(screen, x - 1, y, originColor, newColor); // left paintFill(screen, x + 1, y, originColor, newColor); // right } }
所有代码都在Github上
相关文章推荐
- java recursive call 递归调用
- JAVA中的递归方法应用
- 树形目录的递归实现(一)数据库+jsp+javabean
- Java 学习笔记 (10) - Java 函数的递归调用
- java递归删除目录下所有文件包括目录
- 基于Java Socket的文件UpLoad代码(完美版)-用递归解决java的目录树遍历
- 基于Java Socket的文件UpLoad代码(完美版)-用递归解决java的目录树遍历
- JAVA利用递归删除文件和文件夹
- 我用Java实现的二叉树的遍历(递归和非递归)
- 基于Java Socket的文件UpLoad代码(完美版)-用递归解决java的目录树遍历
- Java小段子-File对象递归删除目录下所有内容
- java中的递归
- Java: 递归删除文件夹内容
- 《Thinking in Java 4th》1~13章读书摘要
- 笔试题之交错数组 Java递归求解
- 树形目录的递归实现(一)数据库+jsp+javabean
- 树形目录的递归实现(一)数据库+jsp+javabean
- 递归在java语言中的应用
- Java文件夹递归删除方法
- 基于Java Socket的文件UpLoad代码(完美版)-用递归解决java的目录树遍历