删除线格式 @[toc]
问题描述
- 现给出m个不同的数字,在n个位置上,对齐进行全排列。
- 使用编程实现数学中全排列
- 输出最终计算结果并将所有的排列打印出来。
思路分析
- 常规的递归方式进行解决即可,递归的终点是根据题目要求进行实现。
- 共有两个参数,m和n。m是需要排列的位子,n是需要排列元素个数,递归的终点是n次。
- 注意 这里是全排列,始终是全排列,和部分排列是有区别的。
实现代码
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
void fullPermutation(int n,vector<int> &vecSour,vector<int> &vecTar){
if(n == 0){
for (auto iter = vecTar.cbegin(); iter != vecTar.cend(); iter++) {
cout<<(*iter)<<","<<endl;
}
}else{
for (int i =0;i < vecSour.size();i ++) {
vecTar.push_back(vecSour.at(i));
vecSour.erase(vecSour.begin()+i);
fullPermutation(n-1,vecSour,vecTar);
vecTar.insert(vecSour.begin()+i,(*vecTar.end()));
vecTar.pop_back();
}
}
}
int main() {
cout << "please input the m and n to full permutation" << std::endl;
int m,n;
cin>>m>>n;
vector<int> iVecSour(m);
vector<int> iVecTar(n);
iota(iVecSour.begin(),iVecSour.end(),1);
fullPermutation(n,iVecSour,iVecTar);
return 0;
}
- 将近一年都没有怎么写过算法题,惭愧,弄了半天,弄出来个啥,真的惭愧,好好复习一下,忘得差不多了。真的。
书上代码
inline void Swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
void Perm(vector<int> vecSour,int k,int m){
/*
* vecSour是保存了原来的数组
* k是表示当前的已经排到了什么位置
* m是需要排几个位置,最后也仅仅输出最终数组的前几个元素
*/
if (k == m){
for(int i = 0;i < m;i ++){
cout<<vecSour.at(i)<<" ";
}
cout<<endl;
}else{
for(int i =k; i <= m;i ++){
Swap(*(vecSour.begin()+k),*(vecSour.begin()+i));
Perm(vecSour,k+1,m);
Swap(*(vecSour.begin()+i),*(vecSour.begin()+k));
}
}
}
思路分析
- 注意,这里是全排列,这个代码也仅仅适用于全排列,像那种4选2,就不适合,书里面对于问题的拆解也是正确的。
- Perm®是由r1Perm(R1) , r2Perm(R2) ,r3Perm(R3) 。。。。rnPerm(Rn)构成,比如说Perm(4)就是由 4 * Perm(3),依次进行递归就是4 * 3 * Perm(2),最后就是4 * 3 * 2 * Perm(1).确实是将问题不断进行拆解递归的很完整。
从书中代码获得的部分排列的代码
思路描述
- 和书里面的代码相似,书里面就是用了一个list,通过改变位置来实现对于所有元素的排列,所以使用一个list,只需要添加一个游标来控制有序的位置和无序的位置即可
实现代码
inline void Swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
void Perm(vector<int> vecSour,int n,int m,int k = 0){
/*
* vecSour是保存了原来的数组
* k是区分是否有序和无序的游标
* m是需要排列的位置
*/
if (k == n){
for(int i = 0;i < n;i ++){
cout<<vecSour.at(i)<<" ";
}
cout<<endl;
}else{
for(int i =k; i < m;i ++){
Swap(*(vecSour.begin() + k),*(vecSour.begin()+i));
Perm(vecSour,n,m,k + 1);
Swap(*(vecSour.begin()+i),*(vecSour.begin()+k));
}
}
}
知识补充和回顾
- inline内联函数的作用:在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
- 学习连接
分析与总结
- 我改了书上的方法,将之应用于部分排列,然后通过交换,仅仅使用一个数组进行排列的效率会更好,时间复杂度都会更低,因为使用一个数组是每一步都是常数的,确定的步骤不变的。但是使用两个数组,有很多操作是线性的,数据越多,耗费的时间越多
- 使用两个数组的,完全模拟实际情况的时间复杂度是O(n)
- 使用一个数组的,通过游标区分有序和无序的时间复杂度是O(1)
- 很久没写代码了,弄了半天就弄出来这个东西,真的惭愧。第一天五十五分钟,弄出来的都是问题,还是要多实践。剩下的部分,明天再写吧。
- 花了好几天才写完这些,后续继续加油!!!!