class038 经典递归解析
算法讲解038【必备】常见经典递归过程解析
code1 字符串的全部子序列
// 字符串的全部子序列
// 子序列本身是可以有重复的,只是这个题目要求去重
// 测试链接 : https://www.nowcoder.com/practice/92e6247998294f2c933906fdedbc6e6a
package class038;
import java.util.HashSet;
// 字符串的全部子序列
// 子序列本身是可以有重复的,只是这个题目要求去重
// 测试链接 : https://www.nowcoder.com/practice/92e6247998294f2c933906fdedbc6e6a
public class Code01_Subsequences {
public static String[] generatePermutation1(String str) {
char[] s = str.toCharArray();
HashSet<String> set = new HashSet<>();
f1(s, 0, new StringBuilder(), set);
int m = set.size();
String[] ans = new String[m];
int i = 0;
for (String cur : set) {
ans[i++] = cur;
}
return ans;
}
// s[i...],之前决定的路径path,set收集结果时去重
public static void f1(char[] s, int i, StringBuilder path, HashSet<String> set) {
if (i == s.length) {
set.add(path.toString());
} else {
path.append(s[i]); // 加到路径中去
f1(s, i + 1, path, set);
path.deleteCharAt(path.length() - 1); // 从路径中移除
f1(s, i + 1, path, set);
}
}
public static String[] generatePermutation2(String str) {
char[] s = str.toCharArray();
HashSet<String> set = new HashSet<>();
f2(s, 0, new char[s.length], 0, set);
int m = set.size();
String[] ans = new String[m];
int i = 0;
for (String cur : set) {
ans[i++] = cur;
}
return ans;
}
public static void f2(char[] s, int i, char[] path, int size, HashSet<String> set) {
if (i == s.length) {
set.add(String.valueOf(path, 0, size));
} else {
path[size] = s[i];
f2(s, i + 1, path, size + 1, set);
f2(s, i + 1, path, size, set);
}
}
}
code2 90. 子集 II
// 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的组合
// 答案 不能 包含重复的组合。返回的答案中,组合可以按 任意顺序 排列
// 注意其实要求返回的不是子集,因为子集一定是不包含相同元素的,要返回的其实是不重复的组合
// 比如输入:nums = [1,2,2]
// 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
// 测试链接 : https://leetcode.cn/problems/subsets-ii/
package class038;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的组合
// 答案 不能 包含重复的组合。返回的答案中,组合可以按 任意顺序 排列
// 注意其实要求返回的不是子集,因为子集一定是不包含相同元素的,要返回的其实是不重复的组合
// 比如输入:nums = [1,2,2]
// 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
// 测试链接 : https://leetcode.cn/problems/subsets-ii/
public class Code02_Combinations {
public static List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums);
f(nums, 0, new int[nums.length], 0, ans);
return ans;
}
public static void f(int[] nums, int i, int[] path, int size, List<List<Integer>> ans) {
if (i == nums.length) {
ArrayList<Integer> cur = new ArrayList<>();
for (int j = 0; j < size; j++) {
cur.add(path[j]);
}
ans.add(cur);
} else {
// 下一组的第一个数的位置
int j = i + 1;
while (j < nums.length && nums[i] == nums[j]) {
j++;
}
// 当前数x,要0个
f(nums, j, path, size, ans);
// 当前数x,要1个、要2个、要3个...都尝试
for (; i < j; i++) {
path[size++] = nums[i];
f(nums, j, path, size, ans);
}
}
}
}
code3 46. 全排列
// 没有重复项数字的全排列
// 测试链接 : https://leetcode.cn/problems/permutations/
package class038;
import java.util.ArrayList;
import java.util.List;
// 没有重复项数字的全排列
// 测试链接 : https://leetcode.cn/problems/permutations/
public class Code03_Permutations {
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
f(nums, 0, ans);
return ans;
}
public static void f(int[] nums, int i, List<List<Integer>> ans) {
if (i == nums.length) {
List<Integer> cur = new ArrayList<>();
for (int num : nums) {
cur.add(num);
}
ans.add(cur);
} else {
for (int j = i; j < nums.length; j++) {
swap(nums, i, j);
f(nums, i + 1, ans);
swap(nums, i, j); // 特别重要,课上进行了详细的图解
}
}
}
public static void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
public static void main(String[] args) {
int[] nums = { 1, 2, 3 };
List<List<Integer>> ans = permute(nums);
for (List<Integer> list : ans) {
for (int num : list) {
System.out.print(num + " ");
}
System.out.println();
}
}
}
code4 47. 全排列 II
// 有重复项数组的去重全排列
// 测试链接 : https://leetcode.cn/problems/permutations-ii/
package class038;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
// 有重复项数组的去重全排列
// 测试链接 : https://leetcode.cn/problems/permutations-ii/
public class Code04_PermutationWithoutRepetition {
public static List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
f(nums, 0, ans);
return ans;
}
public static void f(int[] nums, int i, List<List<Integer>> ans) {
if (i == nums.length) {
List<Integer> cur = new ArrayList<>();
for (int num : nums) {
cur.add(num);
}
ans.add(cur);
} else {
HashSet<Integer> set = new HashSet<>();
for (int j = i; j < nums.length; j++) {
// nums[j]没有来到过i位置,才会去尝试
if (!set.contains(nums[j])) {
set.add(nums[j]);
swap(nums, i, j);
f(nums, i + 1, ans);
swap(nums, i, j);
}
}
}
}
public static void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
code5 用递归函数逆序栈
// 用递归函数排序栈
// 栈只提供push、pop、isEmpty三个方法
// 请完成无序栈的排序,要求排完序之后,从栈顶到栈底从小到大
// 只能使用栈提供的push、pop、isEmpty三个方法、以及递归函数
// 除此之外不能使用任何的容器,数组也不行
// 就是排序过程中只能用:
// 1) 栈提供的push、pop、isEmpty三个方法
// 2) 递归函数,并且返回值最多为单个整数
package class038;
import java.util.Stack;
// 用递归函数逆序栈
public class Code05_ReverseStackWithRecursive {
public static void reverse(Stack<Integer> stack) {
if (stack.isEmpty()) {
return;
}
int num = bottomOut(stack);
reverse(stack);
stack.push(num);
}
// 栈底元素移除掉,上面的元素盖下来
// 返回移除掉的栈底元素
public static int bottomOut(Stack<Integer> stack) {
int ans = stack.pop();
if (stack.isEmpty()) {
return ans;
} else {
int last = bottomOut(stack);
stack.push(ans);
return last;
}
}
public static void main(String[] args) {
Stack<Integer> stack = new Stack<Integer>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
reverse(stack);
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
code6 用递归函数排序栈
// 用递归函数排序栈
// 栈只提供push、pop、isEmpty三个方法
// 请完成无序栈的排序,要求排完序之后,从栈顶到栈底从小到大
// 只能使用栈提供的push、pop、isEmpty三个方法、以及递归函数
// 除此之外不能使用任何的容器,数组也不行
// 就是排序过程中只能用:
// 1) 栈提供的push、pop、isEmpty三个方法
// 2) 递归函数,并且返回值最多为单个整数
package class038;
import java.util.Stack;
// 用递归函数排序栈
// 栈只提供push、pop、isEmpty三个方法
// 请完成无序栈的排序,要求排完序之后,从栈顶到栈底从小到大
// 只能使用栈提供的push、pop、isEmpty三个方法、以及递归函数
// 除此之外不能使用任何的容器,数组也不行
// 就是排序过程中只能用:
// 1) 栈提供的push、pop、isEmpty三个方法
// 2) 递归函数,并且返回值最多为单个整数
public class Code06_SortStackWithRecursive {
public static void sort(Stack<Integer> stack) {
int deep = deep(stack);
while (deep > 0) {
int max = max(stack, deep);
int k = times(stack, deep, max);
down(stack, deep, max, k);
deep -= k;
}
}
// 返回栈的深度
// 不改变栈的数据状况
public static int deep(Stack<Integer> stack) {
if (stack.isEmpty()) {
return 0;
}
int num = stack.pop();
int deep = deep(stack) + 1;
stack.push(num);
return deep;
}
// 从栈当前的顶部开始,往下数deep层
// 返回这deep层里的最大值
public static int max(Stack<Integer> stack, int deep) {
if (deep == 0) {
return Integer.MIN_VALUE;
}
int num = stack.pop();
int restMax = max(stack, deep - 1);
int max = Math.max(num, restMax);
stack.push(num);
return max;
}
// 从栈当前的顶部开始,往下数deep层,已知最大值是max了
// 返回,max出现了几次,不改变栈的数据状况
public static int times(Stack<Integer> stack, int deep, int max) {
if (deep == 0) {
return 0;
}
int num = stack.pop();
int restTimes = times(stack, deep - 1, max);
int times = restTimes + (num == max ? 1 : 0);
stack.push(num);
return times;
}
// 从栈当前的顶部开始,往下数deep层,已知最大值是max,出现了k次
// 请把这k个最大值沉底,剩下的数据状况不变
public static void down(Stack<Integer> stack, int deep, int max, int k) {
if (deep == 0) {
for (int i = 0; i < k; i++) {
stack.push(max);
}
} else {
int num = stack.pop();
down(stack, deep - 1, max, k);
if (num != max) {
stack.push(num);
}
}
}
// 为了测试
// 生成随机栈
public static Stack<Integer> randomStack(int n, int v) {
Stack<Integer> ans = new Stack<Integer>();
for (int i = 0; i < n; i++) {
ans.add((int) (Math.random() * v));
}
return ans;
}
// 为了测试
// 检测栈是不是从顶到底依次有序
public static boolean isSorted(Stack<Integer> stack) {
int step = Integer.MIN_VALUE;
while (!stack.isEmpty()) {
if (step > stack.peek()) {
return false;
}
step = stack.pop();
}
return true;
}
// 为了测试
public static void main(String[] args) {
Stack<Integer> test = new Stack<Integer>();
test.add(1);
test.add(5);
test.add(4);
test.add(5);
test.add(3);
test.add(2);
test.add(3);
test.add(1);
test.add(4);
test.add(2);
sort(test);
while (!test.isEmpty()) {
System.out.println(test.pop());
}
// 随机测试
int N = 20;
int V = 20;
int testTimes = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N);
Stack<Integer> stack = randomStack(n, V);
sort(stack);
if (!isSorted(stack)) {
System.out.println("出错了!");
break;
}
}
System.out.println("测试结束");
}
}
code7 打印n层汉诺塔问题的最优移动轨迹
// 打印n层汉诺塔问题的最优移动轨迹
package class038;
// 打印n层汉诺塔问题的最优移动轨迹
public class Code07_TowerOfHanoi {
public static void hanoi(int n) {
if (n > 0) {
f(n, "左", "右", "中");
}
}
public static void f(int i, String from, String to, String other) {
if (i == 1) {
System.out.println("移动圆盘 1 从 " + from + " 到 " + to);
} else {
f(i - 1, from, other, to);
System.out.println("移动圆盘 " + i + " 从 " + from + " 到 " + to);
f(i - 1, other, to, from);
}
}
public static void main(String[] args) {
int n = 3;
hanoi(n);
}
}