一、问题背景
突发奇想就是想利用递归方法
来求解数字的排列组合的问题,感觉数字排列组合使用递归求解起来是比较方便简洁的,我们只需要定义好终止条件以及我们需要剪枝的一些条件。
问题1:给出一个正数n,我们需要求解出0到n-1范围内多个数字能组合成多少个数。例如,n=3,则可以组成:102、120、210、201四种数字组合。
其实通过我们的分析,由于0不能作为数字的首位,其余数字可以位于任何的位置,则最后的排列情况有 n*n! 次。
下面我们通过递归的方式来复杂化求解过程 虽然把问题从简单到复杂,但这也是有助于我们理解递归的过程,可以推导到更加复杂的排列组合情况。
全排列问题 — 解决方案:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void _combine(int &res, string str, int start, int n, vector<bool> &used) {
if (str.length()==n && str[0]!='0') {
cout<<str<<endl;
res += 1;
return;
}
for (int i=0; i<n; i++) {
if (!used[i]) {
//if(i>0&&nums[i]==nums[i-1]&&!used[i-1])// 这一段代码是用来对所给的排列数字或字母存在重复的进行判重剪枝去掉重复的排列方式。(条件:将开始的序列排序使得相同的字母数字相邻)
// continue;
used[i] = true;
_combine(res, str+to_string(i), start+1, n,used);
used[i] = false;
}
}
}
int findNumbers(int n) {
if (n==0) return 0;
if (n<=2) return 1;
int res=0;
vector<bool> used(n, false);
_combine(res,"",0,n, used);
return res;
}
int main()
{
int res = 0;
res = findNumbers(4);
cout << "nums ="<<res<<endl;
return 0;
}
这种全排列问题我们采用的是回溯的方法来求解,自然而然就让我想到了经典的回溯问题,八皇后的排列问题,跟这个题本质上是一致的,这里我们可以回顾一下:
题目[leetcode 51]
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
攻击的范围是上下左右和四个斜方向在棋盘中的所有位置。
求解方法如下:
#include <bits/stdc++.h>
using namespace std;
void putQueue(int k, int j, vector<vector<int>> &mark) {
vector<int> x = {0,0,1,-1,1,-1,1,-1};
vector<int> y = {1,-1,0,0,1,1,-1,-1};
for (int i=1; i<mark.size(); i++) {
for (int k=0; k<8; k++) {
int new_x = k+i*x[k];
int new_y = j+i*y[k];
if (new_x >=0&&new_x<mark.size()&&new_y >=0&&new_y<mark.size()) {
mark[new_x][new_y] = 1;
}
}
}
}
void generate(int k, int n, vector<string> &loc, vector<vector<string>> &res, vector<vector<int>> &mark) {
if (k==n) {
res.push_back(loc);
return;
}
for (int i=0; i<n; i++) {
if(mark[k][i]==0) {
vector<vector<int>> mark_pre = mark;
loc[k][i] = 'Q';
putQueue(k, j, mark);
generate(k+1, n, loc, res, mark);
mark = mark_pre;
loc[k][i] = '.';
}
}
}
// 这里我们推广到N皇后的问题
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> result; // 保存最终的所有满足情况的结果
vector<vector<int>> mark(n, vector<int> (n, 0)); // 标记棋盘上还能放置皇后的位置
vector<string> location; // 保存棋盘上皇后的位置
for(int i = 0; i < n; i++){
location.push_back("");//棋盘的构造
location[i].append(n, '.');
}
generate(0, n, location, result, mark); //dfs函数调用
return result;
}
问题2:给定两个整数n和k,从n中返回k个数字的所有可能组合。例如,n=4和k=2的情况下,返回的组合即如果为:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4]]
子集 — 解决方案:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void _combine(vector<vector<int>> &res, vector<int> &tmp, int n, int k, int start) {
if (tmp.size()==k) {
res.push_back(tmp);
return;
}
for (int i=start; i<=n; i++) {
tmp.push_back(i);
_combine(res, tmp, n, k, i+1);
tmp.pop_back();
}
}
vector<vector<int>> combineNums(int n, int k) {
vector<vector<int>> res;
vector<int> tmp;
if (k>n || k==0) return res;
_combine(res, tmp, n, k, 1);
return res;
}
int main()
{
vector<vector<int>> res;
res = combineNums(4, 2);
for (int i=0; i<res.size(); i++) {
for (int j=0; j<res[0].size(); j++) {
cout <<res[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
综上所述,有篇文章总结的比较经典:C++ 总结了回溯问题类型 带你搞懂回溯算法(排列篇)
首先我们明白一点,全排列问题和 子集、组合
这是两类问题,我们应该分别对待,总结来说呢就是文章所提的: