排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
private:
void backtracking(vector<vector<int>>& result, vector<int>& nums, vector<int>& temp) {
if (temp.size() == nums.size()) {
result.emplace_back(temp);
return;
}
for (int i = 0; i < nums.size(); ++i) {
if (std::count(temp.begin(), temp.end(), nums[i])) {
continue;
}
temp.emplace_back(nums[i]);
backtracking(result, nums, temp);
temp.pop_back();
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
if (nums.empty()) {
return result;
}
vector<int> temp;
backtracking(result, nums, temp);
return result;
}
};
子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
输入:nums = [1,2,3]
输出:[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
class Solution {
private:
void backtracking(vector<vector<int>>& result, vector<int>& nums, vector<int>& temp, int curLength, int startIndex) {
if (temp.size() == curLength) {
result.emplace_back(temp);
return;
}
for (int i = startIndex; i < nums.size(); ++i) {
temp.emplace_back(nums[i]);
backtracking(result, nums, temp, curLength, i + 1);
temp.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
if (nums.empty()) {
return result;
}
vector<int> temp;
for (int i = 0; i <= nums.size(); ++i) {
backtracking(result, nums, temp, i, 0);
}
return result;
}
};
字符串的去重全排列
public class Permutation {
public static void main(String[] args) {
String s = "abc";
permutation(s.toCharArray(), 0);
}
/*
从第一个字符起,挨个与后面每个字符交换。
*/
private static void permutation(char[] chars, int start) {
if (chars == null || chars.length == 0) return;
if (start == chars.length - 1) {
System.out.println(chars);
} else {
for (int i = start, length = chars.length; i < length; ++i) {
if (isSwap(chars, start, i)) {
CommonUtil.swap(chars, i, start);
permutation(chars, start + 1);
CommonUtil.swap(chars, i, start);
}
}
}
}
// 与它后面非重复出现的数字交换
private static boolean isSwap(char[] chars, int start, int end) {
for (int i = start; i < end; ++i) {
if (chars[i] == chars[end]) {
return false;
}
}
return true;
}
}
八皇后问题
/**
* 八皇后问题:
* 在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,
* 即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。
*
* 求出总共有多少种摆法
*/
public class EightQueue {
public static void main(String[] args) {
int[] arr = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
sortQueue(arr, 0);
}
/*
在[0,8)的数组上,第i个位置上的数字j表示(第i行,第j列)
0 <= i < 8
0 <= j < 8
即j的全排列,每个i上可以放置[0,8)个数字, 8!
共有8!种方法,排除掉:
处于同一行的 --> 不可能,在设置的时候就有8个位置了
处于同一列的 --> 任意两个i上的数字相同,也是不可能的
任意两个皇后处于同一对角线的 --> 间距相等了
*/
private static void sortQueue(int[] arr, int start) {
if (start == arr.length - 1) {
// 过滤
if (isDiagonal(arr)) {
// 是对角线的不输出
return;
}
CommonUtil.printArray(arr);
} else {
for (int i = start, length = arr.length; i < length; ++i) {
CommonUtil.swap(arr, start, i);
sortQueue(arr, start + 1);
CommonUtil.swap(arr, start, i);
}
}
}
private static boolean isDiagonal(int[] arr) {
for (int i = 0, length = arr.length; i < length; ++i) {
for (int j = i + 1; j < length; ++j) {
if (arr[i] - arr[j] == i - j || arr[i] - arr[j] == j - i) {
return true;
}
}
}
return false;
}
}
字符串的组合
/**
* <p>
* 输入一个字符串,输出该字符串中字符的所有组合。
* 举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。
*/
public class Combination {
/*
2^n -1 个
*/
public static void main(String[] args) {
String s = "abc";
combination_bit(s.toCharArray()); // 位运算
combination(s.toCharArray()); // 递归
}
/*
共有 2^n -1 中组合
使用位运算, 001表示a, 010表示b, 100表示c
遍历 [1, 2^n) 个数字,对每个数字进行位运算,找出所有组合
O(2^n) 指数级
*/
private static void combination_bit(char[] chars) {
if (chars == null || chars.length == 0) return;
int length = chars.length;
int n = 1 << length;
StringBuilder builder = new StringBuilder();
for (int i = 1; i < n; ++i) {
for (int j = 0; j < length; ++j) {
if ((i & (1 << j)) != 0) { // 001 010 100
builder.append(chars[j]);
}
}
System.out.println(builder.toString());
builder.delete(0, builder.length());
}
}
/*
在长度为n的字符串中求m个字符的组合, 1 <= m <= n。我们先从头扫描字符串的第一个字符。
针对第一个字符,我们有两种选择:
第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;
第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。
这两种选择都很容易用递归实现。
*/
private static void combination(char[] chars) {
if (chars == null || chars.length == 0) return;
Stack<Character> stack = new Stack<>();
for (int i = 1, length = chars.length; i <= length; ++i) {
combination(chars, 0, i, stack);
}
}
/*
start指扫描到哪个字符了
number是指几位组合, 1位组合还是m位组合
*/
private static void combination(char[] chars, int start, int number, Stack<Character> stack) {
if (number == 0) {
System.out.println(stack.toString());
return;
}
if (start == chars.length) {
return;
}
stack.push(chars[start]);
combination(chars, start + 1, number - 1, stack);
stack.pop();
combination(chars, start + 1, number, stack);
}
}
组合习题一
/**
* 输入两个整数n和m,
* 从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。
*/
public class EqualsMCombination {
public static void main(String[] args) {
// 找出所有组合,输出和为m的组合
findSumIsM(5, 12);
System.out.println();
System.out.println();
// 以和为target寻找
findSumIsM_Simple(5, 12);
}
/*
从最大值开始开始寻找,即从n开始
对于每个n,都有两种选择:
1. 将即计入和m中,在剩下的n-1个数字中继续寻找和为 n-m 的数字
2. 不计算于其中,在剩下的 n-1 个数字中寻找和为m 的数字
终止条件: m==0(找完了)
n<0 || m<0 (找不到)
*/
private static void findSumIsM_Simple(int n, int m) {
if (m > ((1 + n) * n / 2)) {
System.out.println("m太大了");
}
Stack<Integer> stack = new Stack<>();
findIt_Simple(n, m, stack);
}
private static void findIt_Simple(int n, int m, Stack<Integer> stack) {
if (n < 0 || m < 0) {
return;
}
if (m == 0) {
System.out.println(stack);
return;
}
stack.push(n);
findIt_Simple(n - 1, m - n, stack);
stack.pop();
findIt_Simple(n - 1, m, stack);
}
/*
[1,n] 中,获取所有的组合,输出和为m的组合
分两步:
1. 获取所有组合 (就可以使用之前字符串组合的方式)
2. 输出所有和为m的组合
在n个数字中选取m个数字(1<=m<=n), 从1开始遍历数列,针对第一个数字:
将这个数字放到组合中,在剩下的 n-1个 数字中选取m - 1个
不把这个数字放到组合中,在剩下的 n-1 个 数字中选取m个
*/
private static void findSumIsM(int n, int m) {
if (m > ((1 + n) * n / 2)) {
System.out.println("没有满足的序列,超过所有数字之和");
}
Stack<Integer> stack = new Stack<>();
for (int i = 1; i <= n; ++i) {
findIt(n, 1, i, stack, m);
}
}
private static void findIt(int n, int start, int number, Stack<Integer> stack, int targetSum) {
if (number == 0) {
int sum = 0;
for (Integer integer : stack) {
sum += integer;
}
if (sum == targetSum) {
System.out.println(stack);
}
return;
}
if (start == n + 1) {
return;
}
stack.push(start);
findIt(n, start + 1, number - 1, stack, targetSum);
stack.pop();
findIt(n, start + 1, number, stack, targetSum);
}
}
组合习题二
就是一个特殊情况
/**
* Given two integers n and k,
* return all possible combinations of k numbers out of 1 ... n.
* <p>
* 从[1...n]中选取k个数的所有组合
*/
public class LimitedCombination {
public static void main(String[] args) {
int n = 5;
int k = 2;
Stack<Integer> stack = new Stack<>();
combination(n, 0, k, stack);
}
/*
从n个数中选取k个数,从第一个数开始
1) 选择将其放入组合内,从剩下的 n-1 个数中选取 k-1 个数
2) 不将其放入组合内,从剩下的 n-1 个数中 选取 k 个数
*/
private static void combination(int n, int start, int number, Stack<Integer> stack) {
if (number == 0) {
System.out.println(stack);
return;
}
if (start == n + 1) {
return;
}
stack.push(start);
combination(n, start + 1, number - 1, stack);
stack.pop();
combination(n, start + 1, number, stack);
}
}