本文一共提供了4种全排列的方法,包括递归非字典序版本、递归字典序版本、标准库版本和BFS字典序版本,当然BFS非字典序实现相对于BFS字典序版本更加简洁,稍加修改即可。

说明:递归版本基于网上现有代码修改而成,标准库版本参照msdn sample修改而成,最后的BFS版本是由本人在看到题目后思考而来,并实现之(递归版本很久之前写过),所有四种算法都加了模板。当然BFS版本效率相对于递归要快,相对于STL版本则较慢,仅仅提供一种思路而已

注:对于这种小算法,自己主动思考可以开阔思路,而且想出一种新思路感觉会很不错;对于已成型的经典或者复杂算法,新思路的空间会非常小,所以可以以掌握为主。


/**
 @description: generating permunation
 @author: seiyagoo
 @create: 2013.10.10
 @modified: 2013.10.11
 **/

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <iterator>
#include <algorithm>
using namespace std; #define MAX 10
int used[MAX]; //用来标记数字是否已经在前面使用过
int result[MAX]; //存放结果
int array[MAX] = {,,,,,,,,,}; void swap(int x, int y){ int temp = array[x];
array[x]=array[y];
array[y]=temp; return;
} template<typename T>
void printArray(T array[], int size){
int i; for (i=;i<size;i++)
cout << array[i] << " ";
cout << endl; return;
} /*递归(非字典序)*/
template<typename T>
void recur_permute(T array[], int begin, int end)
{
int i;
if (begin==end)
{
printArray(array, end+);
return;
}
else{
//for循环遍历该排列中第一个位置的所有可能情况
for (i=begin; i<=end; i++){
swap(begin, i); //循环变换第一个位置
recur_permute(array, begin+, end); //DFS
swap(begin, i); //回溯,保持原排列
}
}
} /*递归(字典序)*/
template<typename T>
void lexi_recur_permute(T array[], int begin, int end)
{
int i;
if (begin == end+)
{
printArray(result, end+);
return;
}
else{
for (i=; i<=end; i++){
if(!used[i]) //没有标记
{
used[i]=; //标记
result[begin]=array[i]; //记录结果
lexi_recur_permute(array, begin+, end);
used[i]=; //回溯
} }
}
} /*STL(字典序)*/
template<typename T>
void stl_permute(T array[], int size)
{
vector<T>::iterator begin, end;
vector<T> Pattern(size) ;
ostream_iterator<T> out_it(cout, " ") ; //int size=sizeof(array)/sizeof(T); for(int i=; i<size; i++)
Pattern[i]=array[i]; begin = Pattern.begin() ;
end = Pattern.end() ; do
{
copy(begin, end, out_it) ;
cout << endl ;
}while ( next_permutation(begin, end) );
} int get_factorial(int n)
{
if(==n || ==n) return ;
else return n*get_factorial(n-);
} /*给定元素个数n,以及数组p,返回全排列的序号*/
template<typename T>
int perm2num(int n, T *p){
int i, j, num=,k=;
for (i=n-;i>=;i--)//注意下标从0开始
{
for (j=i+;j<n;j++)
if (p[j]<p[i]) num+=k;//是否有逆序,如有,统计
k*=n-i; //每一轮后k=(n-i)!,
}
return num;
} /*BFS(字典序)*/
template<typename T>
void bfs_permute(T array[], int size)
{
int idx=;
int cnt=get_factorial(size); list<T> ls;
queue<list<T>> q; ls.push_back(array[]); q.push(ls);
while(!q.empty())
{
list<T> cur_perm = q.front();
if(cur_perm.size() == size) //第n层的第一个元素长度等于size,循环结束
break;
if(cur_perm.size() != idx) //不相等
idx++; q.pop(); list<T>::iterator it = cur_perm.end();
while( it!=cur_perm.begin() )
{
cur_perm.insert(it, array[idx]); //插入
q.push(cur_perm);
it=cur_perm.erase(--it); //还原 --it; //向前一步找插入点 if( it==cur_perm.begin() ) //第一个插入点特殊处理并结束
{
cur_perm.push_front(array[idx]);
q.push(cur_perm);
cur_perm.clear();
break;
}
}
}
print_queue(q, size, cnt);
} template<typename T>
void print_queue(queue<list<T>> q, int size, int cnt)
{
vector<list<T>> vec(cnt);
T* perm=new T[size]; //存储当前排列 /*映射*/
while(!q.empty())
{
list<T> cur_perm=q.front();
q.pop();
list<T>::iterator it=cur_perm.begin(); int idx=,i=;
int n=size;
while(it!=cur_perm.end())
{
perm[i++]=*it++;
} //当前排列放入全排列对应位置
idx=perm2num(size, perm);
vec[idx]=cur_perm;
}
delete []perm; /*输出*/
vector<list<T>>::iterator vit=vec.begin();
for(;vit!=vec.end();vit++)
{
list<T> cur_perm=*vit;
list<T>::iterator lit=cur_perm.begin();
for(;lit!=cur_perm.end();lit++)
{
cout<<*lit<<" ";
}
cout<<endl;
}
} int main(){
recur_permute(array, , );
lexi_recur_permute(array, ,);
stl_permute(array, );
bfs_permute(array, );
return ;
}

最后再附上STL版本算法思路及修改后的代码(仅仅为了可读性):

思路

给定已知序列P =  A1A2A3.....An
对P按字典排序,得到P的一个最小排列Pmin = A1A2A3....An ,满足Ai > A(i-1) (1 < i <= n)
从Pmin开始,找到刚好比Pmin大的一个排列P(min+1),再找到刚好比P(min+1)大的一个排列,如此重复。
1.从后向前(即从An->A1),找到第一对为升序的相邻元素,即Ai < A(i+1)。
  若找不到这样的Ai,说明已经找到最后一个全排列,可以返回了。
2.从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。
3.将排列中A(i+1)A(i+2)....An这个序列的数逆序倒置,即An.....A(i+2)A(i+1)。因为由前面第1、2可以得知,A(i+1)>=A(i+2)>=.....>=An,这为一个升序序列,应将该序列逆序倒置,所得到的新排列才刚刚好比上个排列大。
4.重复步骤1-3,直到返回。
这个算法是C++ STL算法next_permutation的思想。
 
代码
template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first,
BidirectionalIterator last) {
BidirectionalIterator i=last;
if (first == last || first==--i) return false; //单个元素或空则无下一个排列,且使i指向最后一个元素
for(;;) {
BidirectionalIterator ii = i--; //i在前,ii在后,循环查找直至*i>=*ii
if (*i <*ii) { //第一个非降序的元素(即*i)
BidirectionalIterator j = last;
while (!(*i <*--j)); //在降序序列[ii,last)中从后往前查找第一个大于*i的数
iter_swap(i, j); //交换*i,*j,则[ii,last)依然为降序序列
reverse(ii, last); //翻转[ii,last)为升序
return true;
}
if (i == first) { //没找到连续的两个升序数,则已经是降序序列,直接翻转全部序列
reverse(first, last);
return false;
}
}
}

generating permunation——全排列(算法汇总)的更多相关文章

  1. generating permunation

    generating permunation——全排列(算法汇总) #include <iostream> #include <string> #include <vec ...

  2. 排序算法汇总(C/C++实现)

    前言:     本人自接触算法近2年以来,在不断学习中越多地发觉各种算法中的美妙.之所以在这方面过多的投入,主要还是基于自身对高级程序设计的热爱,对数学的沉迷.回想一下,先后也曾参加过ACM大大小小的 ...

  3. 全排列算法的JS实现

    问题描述:给定一个字符串,输出该字符串所有排列的可能.如输入“abc”,输出“abc,acb,bca,bac,cab,cba”. 虽然原理很简单,然而我还是折腾了好一会才实现这个算法……这里主要记录的 ...

  4. 不会全排列算法(Javascript实现),我教你呀!

    今天我很郁闷,在实验室凑合睡了一晚,准备白天大干一场,结果一整天就只做出了一道算法题.看来还是经验不足呀,同志仍需努力呀. 算法题目要求是这样的: Return the number of total ...

  5. 边缘检测matlab算法汇总

    边缘检测matlab算法汇总 1.      基于一阶微分算子检测边缘图像 一阶微分边缘算子又称梯度边缘算子,它是利用图像在边缘处的阶跃性,及图像梯度在边缘去得极大值得特征性进行边缘检测. Sobel ...

  6. JavaScript 数据结构与算法之美 - 十大经典排序算法汇总(图文并茂)

    1. 前言 算法为王. 想学好前端,先练好内功,内功不行,就算招式练的再花哨,终究成不了高手:只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 ...

  7. PHP字符串全排列算法

    <?php /** * PHP字符串全排列算法 */ $results = []; $arr = []; function bfs($start) { global $arr; global $ ...

  8. 全排列算法--递归实现(Java)

    求一个n阶行列式,一个比较简单的方法就是使用全排列的方法,那么简述以下全排列算法的递归实现. 首先举一个简单的例子说明算法的原理,既然是递归,首先说明一下出口条件.以[1, 2]为例 首先展示一下主要 ...

  9. 排列算法汇总(下一个排列,全排列,第K个排列)

    一.下一个排列 首先,STL提供了两个用来计算排列组合关系的算法,分别是next_permutation和prev_permutation. next_permutation(nums.begin() ...

随机推荐

  1. windows安装memcached

    http://www.cnblogs.com/wujuntian/p/4791220.html

  2. terminfo 数据库?

    什么是 terminfo 数据库? UNIX 系统上的 terminfo 数据库用于定义终端和打印机的属性及功能,包括各设备(例如,终端和打印机)的行数和列数以及要发送至该设备的文本的属性.UNIX ...

  3. Linux - 用 Konstruct 安装 KDE 3.x

    make-kde3.x Linux - 用 Konstruct 安装 KDE 3.x 找个截图工具 khtml2png (http://khtml2png.sourceforge.net/), 现在都 ...

  4. system.setting-全局变量数据监听

    今天在setting里添加了一个新的变量,想要实现对这个变量的监听.现在记录下方法 首先就是明白一点,我们在system.setting里添加的变量,都会被保存在data/data/com.andro ...

  5. Catch Me If You ... Can't Do Otherwise--转载

    原文地址:https://dzone.com/articles/catch-me-if-you-cant-do-otherwise I don't know whether it's an anti- ...

  6. (转)修改 ubuntu 默认启动项

    转自: http://jingyan.baidu.com/article/afd8f4de58959134e386e969.html 当我们安装windows和ubuntu双系统以后,默认启动变成ub ...

  7. 洛谷 P2384 最短路

    洛谷 P2384 最短路 题目背景 狗哥做烂了最短路,突然机智的考了Bosh一道,没想到把Bosh考住了...你能帮Bosh解决吗? 他会给你10000000000000000000000000000 ...

  8. 【AIM Tech Round 4 (Div. 1) B】Interactive LowerBound

    [链接]http://codeforces.com/contest/843/problem/B [题意] 给你一个数组模拟的单链表,放在一个长度为n的数组里面,然后告诉你表头的位置在哪里; 你可以最多 ...

  9. Eclipse导入Maven项目出现错误:Unsupported IClasspathEntry kind=4

    使用Eclipse导入Maven项目失败,提示: An internal error occurred during: "Importing Maven projects". Un ...

  10. 第三次作业 201731082208 黄亚恒&肖莉

    Github项目地址:https://github.com/HYHSTUDEY/WordCount.git 作业地址:https://www.cnblogs.com/hyhhyh090628/p/10 ...