您的位置:首页 > 理论基础 > 数据结构算法

数据结构复习笔记(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)

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上
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息