全排列有比较多的思路,最常规能想到的就是穷举,这是最笨的方法,也是最接近人思维的方法,但是代码真的一般,时间复杂度和空间复杂度都不是很好;其次就是容易想到的递归,递归的思路有多种,第一种可以递增递归,就是假设我们N个元素的全排列,我们假设原来的序列的顺序固定,我们从第1个开始,一个一个的将元素加入到全排列的序列里面去。还有一种递归的思路就是数学组合问题,Cij的问题,从I个元素中选取J个元素的可能次数,这种不在这里累述,因为比较接近数学思维,想起来也比较容易,递归思路我能想到的就以上2个,如果有后续,后续补充。第三种解法就是回溯,这个比较接近实际操作,我们从已经排序好的全排序的结果一个个的回溯回到第一个元素进入全排序队列的方法;还有一种是采用二进制的0/1性,来控制组合数,二进制比较巧妙的0/1可以看出每个位置上元素的选取和未被选取,比如我们要从N个元素中选取M个元素(全排列中M=N),我们就在N位的二进制的最低M位全部置1,然后我们从左到右无限找01的情况,找到了就交换位置,每找到一次就是我们一次新的全排列情况,知道不能找到01的组合,这种情况比较巧妙,是假和空间复杂度是最小的,但是比较生僻。
下面我就递归思路贴一点网上比较多的代码,自己调试测试通过lintcode,可以直接跑过。
using namespace std;
#include<vector>
#include<time.h>
class Solution {
public:
vector<vector<int>> permute(vector<int> &nums) {
// write your code here
vector<vector<int>> out;
//考虑使用递归方法,将递归函数独立出来
permut(nums, nums.size(), 0, out);
return out;
}
private:
/* --------------------------------------------------------------------------------
采用倒排递归:
考虑从0开始进行全排列,我们依次选取N个元素(N为最大元素的个数),这样我们必须假设最开始
的数据顺序是固定的,我们必须基于这个固定的顺序上进行选取,否则会混乱。第一次选取第一个,
第二次选取第二个... ... 以此类推,我们能一直选取到最后一个元素:所以递归中退出的条件为:
当前选取元素的下标为最后一个元素,我们就退出,将当前序列记录下来,就是我们该次的全排列。
那么考虑中间的任何一个情况,既有:假设这个元素是当前第M个元素(M<=N),第M个元素必须要、
做的是事情分为:1)他需要和后面未构成全排列内容的元素进行位置互换,构成新的可能性。在每、
一种可能性里面,需要对M+1个元素进行全排列(递归),针对第M个元素做完了交换和M+1的全排列
之后,我们需要将改元素和原来交换的元素恢复回来,也就是重新交换,这样能保证列表的顺序,
因为我们的排列的最基本的条件之一就是该序列表顺序是给定的。一次类推,全排列就出来了。
-------------------------------------------------------------------------------
将数组分成两个部分,0-start部分位已经选择好的区间,start-end为待选择的区间
每次从待选择的区间选择一个加入,然后递归下一个回合;
-----------------------------------------------------------------------------------*/
int permut(vector<int> &in, int size, int start, vector<vector<int>> &out) {
if (size <= start) {
out.push_back(in);
return 0;
}
//核心区块
for (int i = start; i < size; i++) {
swap(in[i], in[start]);
permut(in, size, start + 1, out);
swap(in[i], in[start]);
}
return 0;
}
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
return;
}
};
int main() {
Solution s = Solution();
vector<int> in = {0,1};
s.permute(in);
return 0;
}