排列
我的处理方式主要是通过递归方式进行遍历。同时利用boolean[]数组,来判断某个数字有没有被用过。辅助以Stack,进行数据的存储,注意Stack可以是正常的Stack类,也可以是一般的数组。算法复杂度O(P(n, k))。
public void iteratePermutation(int n, int k) {// P(n, k)
boolean[] used = new boolean[n];
int[] permutation = new int[k];
for (int i = 0 ; i < n; i++) {
choose(i, k , used, permutation);
}
}
private void choose(int i , int k ,boolean[] used, int[] permutation) {
used[i] = true;
permutation[permutation.length - k] = (i+1);
k--;
if (k == 0) {
doSomething(permutation);
used[i] = false;
return;
}
for (int j = 0 ; j < used.length; j++) {// 排列与组合的最大区别是,排列可以用到以前的数字,而组合不能。
if (!used[j]) {
choose(j, k, used, permutation);
}
}
used[i] = false;// 这一步容易忘记
}
private void doSomething(int[] permutation) {
StringBuilder sb = new StringBuilder();
for (int i : permutation) {
sb.append(i).append(' ');
}
System.out.println(sb);
}
组合
组合的话,只要在排列的基础上稍加改动就行,不要用到下标 i 及以前的元素。int j = i+1。可以对这个方法进行剪枝操作,如果剩下的元素不足以凑够k个,那么直接返回。否则,复杂度就会变成O(Combination(n, k)+ Combination(n, k-1) + ... + Combination(n, 1))。
for (int j = i+1 ; j < used.length; j++) {//组合从i+1开始遍历。
组合的另外一种实现方式:
利用递归的方式,每个元素可取可不取。对于Combination(n, k)来说,只要拿到了k个元素就可以了。
这种方法必须进行剪枝操作,就是当剩余元素不足k个时,终止递归。如果不进行剪枝,复杂度会上升到O(2^n)。
class Solution {
Stack<Integer> stack = new Stack<>();
int n;
public void iterateCombination(int n, int k) {
this.n = n;
visit(0, k);
}
private void visit(int cur, int k) {
if ((n - cur) < k) {// 剪枝
return;
}
if (k == 0) {
System.out.println(stack);
return;
}
// if (cur == n) return;
stack.push(cur+1);// 有这个元素
visit(cur+1, k-1);
stack.pop();
visit(cur+1, k);// 没有这个元素
}
}
数组的子集
在一个大小为n的数组中,任意一个元素可以存在或者不存在,所以子集中一共有2^n个元素。那么如何获取呢?
利用掩码
思路是,得到0~2^n-1个数,int i = 0 ; i < (1<<n);i++, 进行遍历。将i视作一个二进制掩码,如果掩码为1,那么获取这个值,否则抛弃这个值。
public void iterateSubset(int[] nums) {
for (int i = 0 ; i < (1<<nums.length); i++) {
int mask = i;
List<Integer> element = new LinkedList<>();
for (int j = 0 ; j < nums.length; j++) {
if ((mask & 1) == 1) { // 掩码为1,添加这个值
element.add(nums[j]);
}
mask = mask >> 1;
}
doSomething(element);
}
}
利用递归
元素可以在,也可以不在,这一点可以在递归中实现。这种输出顺序好像会比上面的好些,看你是否有访问顺序的需要了。
class Solution {
Stack<Integer> stack = new Stack<>();
public void iterateSubset(int[] nums) {
visit(0, nums);
}
private void visit(int cur, int[] nums) {
if (cur == nums.length) {
System.out.println(stack);
return;
}
stack.push(nums[cur]);// 有这个元素
visit(cur+1, nums);
stack.pop();
visit(cur+1, nums);// 没有这个元素
}
}