博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Permutations(排列问题,DFS回溯)
阅读量:6835 次
发布时间:2019-06-26

本文共 5310 字,大约阅读时间需要 17 分钟。

Given a collection of numbers, return all possible permutations.

For example,

[1,2,3] have the following permutations:
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].

分析图如下,简单的说就是第一个数分别和后面的数交换

拿[1,2,3,4]举例,当我们固定第一个数之后,相当于后三个数做全排列,是个子问题,即在以该三个数的第一个数来分别和后面的数交换即可。

注意:回溯的时候(即执行完dfs之后的一句),必须把发生交换的序列,变回原样。

代码(有重复则错误):

class Solution {private:    int len;    vector
> res;public: void dfs(int dep,vector
num) { if(dep==len){ res.push_back(num); return; } for (int i=dep;i
> permute(vector
&num) { len=num.size(); dfs(0,num); return res; }};

 

 


排列问题递归和非递归的详细解法()

全排列在近几年各大网络公司的笔试中出现的比较频繁

首先来看看题目是如何要求的(百度迅雷校招笔试题)。

用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,

如 abc 的全排列: abc, acb, bca, dac, cab, cba

一、      递归版本

1、算法简述

简单地说:就是第一个数分别以后面的数进行交换

E.g:E = (a , b , c),则 prem(E)= a.perm(b,c)+ b.perm(a,c)+ c.perm(a,b)

然后a.perm(b,c)= ab.perm(c)+ ac.perm(b)= abc + acb.依次递归进行

 

好了,知道算法之后就不难编出一份好的代码了。

2、代码参考

int len;void dfs(int dep,char* s){    if(dep==len){        static int s_i = 1;        cout<<"第"<
<<"个全排列为: "<
<

 

3、见图知晓

不过这样存在一点小小的缺陷:两个相同的数也进行了交换,见下图:

明显,这绝对不符合要求:

4、代码改进

去掉重复符号的全排列:在交换之前可以先判断两个符号是否相同,不相同才交换,这个时候需要一个判断符号是否相同的函数。

bool IsSwap(char *pszStr, int nBegin, int nEnd)  {         for (int i = nBegin; i < nEnd; i++)        if (pszStr[i] == pszStr[nEnd])               return false;         return true;  }

所以,改进的代码如下:

1 Perm(char *pszStr, int k, int m)   2 {   3      if (k == m)   4      {   5           Static int s_i = 1;   6           cout<<” 第 ”<

OK,见图知情况

 二、 非递归版本

1、算法简述

要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。

如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。

如果达到这个数的最大,比如1234-à4321,这个时候就结束整个循环。

如果输入是一个非最小数,如1324,则将它转换为最小数,如1234,再进行排序。排序算法用快排,可以自己写一个,如果快排不会的话,就先看会再来接着看,或者自己想一个靠谱的算法,也可以直接用VC库中的qsort(s , n , sizeof(s[0]) , cmp);各参数是什么意思就自己在下面多花点时间吧。

OK,下面看代码分析

2、代码分析

1 Prem( char *s )   //全排列函数 2 { 3     char *pEnd = s + strlen(s) - 1; 4     char *p = pEnd;  //p代表替换点 5     //q代表替换点的下一个数 ,pMax 代表替换点后比替换点大的最小数 6     char *q = new char,*pMax = new char;  //注意初始化!!! 7     while (p !=  s)          //p == s 就结束循环 8     { 9         q = p;10         p--;11         if (*p < *q)12         {13             pMax = FindMaxForOne(p,pEnd);  //找与替换点交换的点14             Swap(p,pMax);         //交换15             Reverse(q,pEnd);       //将替换点后所有数进行反转16             Print(s);              //输出17             p = pEnd;             //将替换点置最后一个点,开始下一轮循环18         }19         if (s == p) break;           //结束条件20     }21 }

上面涉及到几个函数

说一下找与替换数交换的数的函数

1 char* FindMaxForOne(char *p,char *q)2 {3     char *p1 = p;4     char *p2 = q;5     while (*p2 <= *p1) p2--;6     return p2;7 }

!!!这里要说明:从后往前找的第一个比替换数大的数一定就是要找的最小数,Why,这个慢慢品味,我做的时候也遇到一定的困难,自己去做,不经历就不会轻易铭记。

其他函数简直就是小case了。祝君成功!

3、见图知晓

 

三、非递归还有一种方法

  描述:和上一种不同的是:这种算法比较笨,但很好理解,不用按照上一种那么严格从小到大进行排列输出。

  首先先将最后一个数从右往左依次交换输出,然后判断个数是否为基数,交换离该数最远端的两个数,再把第一个数从左往右交换输出,交换远端的两个数,如此进行循环就能排列完全部的数。这说得可能比较抽象,看一个例子:

  E.g:            1 2 3 4

第一次:(从右往左):1 2 4 3   ---  1 2 4 3 --- 1 4 2 3  ---  4 1 2 3   把最后一个数依次往前移

           交换:2 和 3  --->   4 1 3 2

第二次:(从左往右):4 1 3 2  ---  1 4 3 2  ---  1 3 4 2  ---  1 3 2 4  把第一个数依次往后移

           交换:1 和 3  ----> 3 1 2 4           重复第一次,知道把所有数输出为止

看代码:

1 /************************************************************************ 2  *   Author: bakari  3  *   Date:   2011.5.7 4 /************************************************************************/ 5 int n; 6 void swap(int *a,int *b);    //交换函数  7 void print(int a[]);         //打印交换后的每一组数  8 int jfc();                   //求阶乘函数  9 int jmp(int n);              //跳转函数 10 void sort(int a[]);          //全排列函数 11 12 int main(){13     while(cin>>n)14     {15         while(n<=0)16         {17             cout<<"输入有误!请重新输入: ";18             cin>>n;19         }20         int *a=new int[n];21         for(int i=0;i
jfc())53 return 0;54 }55 void sort(int a[])56 {57 int m=1,count=0; //m统计全排列的个数,count统计行数 58 int *p1,*p2;59 for(p1=a+n-1,p2=a+n-2;p1>=a+1,p2>=a;p1--,p2--)60 {61 print(a);62 swap(p1,p2);63 m++;64 } 65 count++;66 while(m<=jfc()){67 if(count%2)68 { print(a);69 swap(&a[n-1],&a[n-2]);70 m++;71 if(!jmp(m))72 break;73 for(p1=a,p2=a+1;p1<=a+n-2,p2<=a+n-1;p1++,p2++)74 {75 print(a);76 swap(p1,p2);77 m++;78 }79 count++;80 }81 else82 {83 print(a);84 swap(&a[0],&a[1]);85 m++;86 if(!jmp(m))87 break;88 for(p1=a+n-1,p2=a+n-2;p1>=a+1,p2>=a;p1--,p2--)89 {90 print(a);91 swap(p1,p2);92 m++;93 }94 count++;95 }96 97 } 98 cout<<"共有"<
<<"种排列"<

关键是掌握上面两种!

 

四、   总结

至此我们已经运用了递归与非递归的方法解决了全排列问题,总结一下就是:

1.全排列就是从第一个数字起每个数分别与它后面的数字交换。

2.去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

3.全排列的非递归就是由后向前找替换数替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。

                                                                                                                                                                      

转载于:https://www.cnblogs.com/fightformylife/p/4120381.html

你可能感兴趣的文章
《塞洛特傳說》道具系统
查看>>
MCollective架构篇4-MCollective各种插件的部署及测试
查看>>
第五章 Python函数你知多少
查看>>
百度推出飓风算法,严厉打击恶劣采集
查看>>
Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(4)...
查看>>
组策略部署软件----将部署的软件分类
查看>>
系统管理员在企业中的职业定位及发展方向 连载(二)
查看>>
完美世界推穿戴式设备:能消灭“宅玩家”吗?
查看>>
关东升的《从零开始学Swift》3月9日已经上架
查看>>
IFA与“色“俱进,三星“量子点+曲面”如何掀起新变革?
查看>>
2013年4月工作小结 -- 穿越前的回眸
查看>>
用什么样的个人笔记类软件?OneNote、EverNote(印象笔记)、为知笔记、麦库记事、有道云笔记……...
查看>>
Photoshop制作一只可爱的卡通小鸟
查看>>
管理不能太重原则
查看>>
在安装完成oracle的时候,需要su - oracle,但有时候出现ulimit pize...
查看>>
Hadoop系列之六:分布式文件系统HDFS
查看>>
【VMCloud云平台】SCAP(四)连接公有云(一)
查看>>
第十一集VLAN原理和VTP协议理论讲解
查看>>
做网络主播心得
查看>>
Office Web Apps证书的申请步骤讲解
查看>>