组合算法
常用的排列数组合数、子集的个数问题
leetcode 46题 [1,2,3] 所有的全排列问题
这是一个不需要去重的全排列问题.
拿[1,2,3]举例子, 1,2,3 接着是 1,3,2 2,1,3 2,3,1 可以找出规律 2,3交换生成3,2....
每次交换 然后接着下一次进行
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
permuteReclusive(result,nums, 0, nums.length-1);
return result;
}
public void permuteReclusive(List<List<Integer>> result, int[] nums, int start, int end){
if(start==end+1){ // 长度
List<Integer> param=new ArrayList<Integer>();
for(int i=0;i<nums.length;i++){
param.add(nums[i]);
}
result.add(param);
return ;
}
for(int i=start;i<=end;i++){
if(i!=start){ // 交换
int tmp=nums[i];
nums[i]=nums[start];
nums[start]=tmp;
}
// 接着下一波的数据
permuteReclusive(result, nums, start+1,end);
if(i!=start){ // 交换回来
int tmp=nums[i];
nums[i]=nums[start];
nums[start]=tmp;
}
}
}
}对于leetcode 31 本质上也是求全排列中的某个排列下的下一个排列
主要思路是先从尾部到头部找出nums[i]>nums[i-1]的第一次出现的位置设为d=i-1;
接着从尾部到头部找出大于nums[d]的第一个元素出现的位置, m. 把nums[m]和nums[d]交换, 然后再把d+1开始的数据逆序. 我们可以通过这种方法实现全排列问题.代码如下
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
int[] copy = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
copy[i] = nums[i];
}
boolean istart = true;
while (istart || isSame(copy, nums)) {
istart = false;
List<Integer> param = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i++) {
param.add(nums[i]);
}
result.add(param);
nextPermute(nums);
}
return result;
}
public boolean isSame(int[] copy, int[] nums) {
for (int i = 0; i < nums.length; i++) {
if (copy[i] != nums[i]) return true;
}
return false;
}
public void nextPermute(int[] nums) {
int d = -1; // 表示分割点
int m = -1; // 表示大于点
int len = nums.length;
for (int i = len - 2; i >= 0; i--) {
if (nums[i + 1] > nums[i]) {
d = i;
break;
}
}
if (d == -1) {
for (int i = 0; i < (len - 1) / 2; i++) {
int tmp = nums[i];
nums[i] = nums[len - 1 - i];
nums[len - 1 - i] = tmp;
}
return;
}
for (int i = len - 1; i >= 0; i--) {
if (nums[i] > nums[d]) {
m = i;
break;
}
}
int tmp = nums[d];
nums[d] = nums[m];
nums[m] = tmp;
// 逆序
int dest = (len - 1 + d + 1) / 2;
for (int i = d + 1; i <= dest; i++) {
tmp = nums[i];
nums[i] = nums[len - 1 - i + d + 1];
nums[len - 1 - i + d + 1] = tmp;
}
}
}实现全排列代码
class Solution {
public List<List<integer>> permute(int[] nums) {</integer>
List<List<Integer>> result = new ArrayList<>();
int[] copy = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
copy[i] = nums[i];
}
boolean istart = true;
while (istart || isSame(copy, nums)) {
istart = false;
List<Integer> param = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i++) {
param.add(nums[i]);
}
result.add(param);
nextPermute(nums);
}
return result;
}
public boolean isSame(int[] copy, int[] nums) {
for (int i = 0; i < nums.length; i++) {
if (copy[i] != nums[i]) return true;
}
return false;
}
public void nextPermute(int[] nums) {
int d = -1; // 表示分割点
int m = -1; // 表示大于点
int len = nums.length;
for (int i = len - 2; i >= 0; i--) {
if (nums[i + 1] > nums[i]) {
d = i;
break;
}
}
if (d == -1) {
for (int i = 0; i <=(len - 1) / 2; i++) {
int tmp = nums[i];
nums[i] = nums[len - 1 - i];
nums[len - 1 - i] = tmp;
}
return;
}
for (int i = len - 1; i >= 0; i--) {
if (nums[i] > nums[d]) {
m = i;
break;
}
}
int tmp = nums[d];
nums[d] = nums[m];
nums[m] = tmp;
// 逆序
int dest = (len - 1 + d + 1) / 2;
for (int i = d + 1; i <=dest; i++) {
tmp = nums[i];
nums[i] = nums[len - 1 - i + d + 1];
nums[len - 1 - i + d + 1] = tmp;
}
}}
leetcode 47 Permutations II 去重之后的全排列个数
这道题有两种解法
- 在46题基础上加一个判断是否已经存在相同元素, 存在就没必要再次交换。
public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> result=new ArrayList<>();
permuteReclusiveUnique(result, nums, 0);
return result;
}
public void permuteReclusiveUnique(List<List<Integer>>result, int[]nums, int start){
if(start==nums.length){
// 数据保存
List<Integer>params=new ArrayList<>();
for(int i=0;i<nums.length;i++){
params.add(nums[i]);
}
result.add(params);
}
for(int i=start;i<nums.length;i++){
if(!isOk(nums, start, i)){
continue;
}
if(i!=start){
int tmpt=nums[i];
nums[i]=nums[start];
nums[start]=tmpt;
}
permuteReclusiveUnique(result, nums, start+1);
if(i!=start){
int tmpt=nums[i];
nums[i]=nums[start];
nums[start]=tmpt;
}
}
}
public boolean isOk(int[]nums, int start, int end){
for(int i=start;i<end;i++){
if(nums[i]==nums[end]) return false;
}
return true;
}在31题基础上直接获取所有可能情况, 直到等于原来的情况.
public List<List<integer>> permuteUnique(int[] nums) {</integer>List<List<Integer>> result = new ArrayList<>(); int[] copy = new int[nums.length]; for (int i = 0; i < nums.length; i++) { copy[i] = nums[i]; } boolean istart = true; while (istart || isSame(copy, nums)) { istart = false; List<Integer> param = new ArrayList<Integer>(); for (int i = 0; i < nums.length; i++) { param.add(nums[i]); } result.add(param); nextPermute(nums); } return result;}
public boolean isSame(int[] copy, int[] nums) {
for (int i = 0; i < nums.length; i++) {
if (copy[i] != nums[i]) return true;
}
return false;
}
public void nextPermute(int[] nums) {
int d = -1; // 表示分割点
int m = -1; // 表示大于点
int len = nums.length;
for (int i = len - 2; i >= 0; i--) {
if (nums[i + 1] > nums[i]) {
d = i;
break;
}
}
if (d == -1) {
for (int i = 0; i <=(len - 1) / 2; i++) {
int tmp = nums[i];
nums[i] = nums[len - 1 - i];
nums[len - 1 - i] = tmp;
}
return;
}
for (int i = len - 1; i >= 0; i--) {
if (nums[i] > nums[d]) {
m = i;
break;
}
}
int tmp = nums[d];
nums[d] = nums[m];
nums[m] = tmp;
// 逆序
int dest = (len - 1 + d + 1) / 2;
for (int i = d + 1; i <=dest; i++) {
tmp = nums[i];
nums[i] = nums[len - 1 - i + d + 1];
nums[len - 1 - i + d + 1] = tmp;
}
}leetcode 78题 求所有子集
思路 本质上是每个元素取或者不取两种情况
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> results=new ArrayList<>();
List<Integer> param=new ArrayList<>();
ReclusiveTheSubSet(results, param, nums, 0);
return results;
}
public void ReclusiveTheSubSet(List<List<Integer>> results,List<Integer> param, int[] nums, int start){
results.add(new ArrayList<>(param));
for(int i=start;i<nums.length;i++){
param.add(nums[i]); // 取这个元素
ReclusiveTheSubSet(results, param, nums, i+1);
param.remove(param.size()-1); // 不取这个元素
}
}
}leetcode 90 subset去重
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>>results=new ArrayList<>();
// 必须先排序
Arrays.sort(nums);
List<Integer> param=new ArrayList<>();
ReclusiveTheSubset(results, param, nums, 0);
return results;
}
public void ReclusiveTheSubset(List<List<Integer>> results, List<Integer>param, int[]nums, int start){
results.add(new ArrayList<>(param));
for(int i=start;i<nums.length;i++){
// 去重操作
if(isOk(nums, start, i)){continue;}
param.add(nums[i]);
ReclusiveTheSubset(results, param, nums, i+1);
param.remove(param.size()-1);
}
}
public boolean isOk(int[]nums, int start, int end){
for(int i=start;i<end;i++){
if(nums[i]==nums[end]) return true;
}
return false;
}
}