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<vector<int>> res;
public:
void dfs(int dep,vector<int> num)
{
if(dep==len){
res.push_back(num);
return;
}
for (int i=dep;i<len;++i)
{
swap(num[dep],num[i]);
dfs(dep+,num);
swap(num[dep],num[i]);//每次循环开始时候,总是要恢复之前的交换
}
}
vector<vector<int>> permute(vector<int> &num) {
len=num.size();
dfs(,num);
return res;
}
};

排列问题递归和非递归的详细解法(http://www.cnblogs.com/bakari/archive/2012/08/02/2620826.html

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

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

用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 = ;
cout<<"第"<<s_i++<<"个全排列为: "<<s<<endl;
return;
}
for (int i=dep;i<len;++i)
{
swap(s[dep],s[i]);
dfs(dep+,s);
swap(s[dep],s[i]);//回溯的时候,需要把序列置换回来。
}
}
void Foo(char* s)
{
len=strlen(s);
dfs(,s); }

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<<” 第 ”<<s_i ++<<” 个排列 ”<<pszStr<<endl;
7 }
8 else
9 {
10 for (int i = k; i <= m; i++) //第i个数分别与它后面的数字交换就能得到新的排列
11 {
12 if (IsSwap(pszStr, k, i)) //添加的判断语句,判断是否相等
13 {
14 Swap(pszStr + k, pszStr + i);
15 Perm(pszStr, k + 1, m);
16 Swap(pszStr + k, pszStr + i);
17 }
18 }
19 }
20 }

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<n;i++)
22 a[i]=i+1;
23 sort(a);
24 delete []a;
25 }
26 system("pause");
27 return 0;
28 }
29
30 void swap(int *a,int *b)
31 {
32 int t=*a;
33 *a=*b;
34 *b=t;
35 }
36 void print(int a[])
37 {
38 for(int i=0;i<n;i++)
39 cout<<a[i]<<' ';
40 cout<<endl;
41
42 }
43 int jfc()
44 {
45 int s=1;
46 for(int i=1;i<=n;i++)
47 s*=i;
48 return s;
49 }
50 int jmp(int n)
51 {
52 if(n>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 else
82 {
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<<"共有"<<m-1<<"种排列"<<endl;
99 }

关键是掌握上面两种!

四、   总结

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

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

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

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

Permutations(排列问题,DFS回溯)的更多相关文章

  1. 素数环(dfs+回溯)

    题目描述: 输入正整数n,把整数1,2...n组成一个环,使得相邻两个数和为素数.输出时从整数1开始逆时针排列并且不能重复: 例样输入: 6 例样输出: 1 4 3 2 5 6 1 6 5 2 3 4 ...

  2. NOJ 1074 Hey Judge(DFS回溯)

    Problem 1074: Hey Judge Time Limits:  1000 MS   Memory Limits:  65536 KB 64-bit interger IO format: ...

  3. HDU 1016 Prime Ring Problem(经典DFS+回溯)

    Prime Ring Problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  4. HDU 2181 哈密顿绕行世界问题(经典DFS+回溯)

    哈密顿绕行世界问题 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  5. HDU1016 Prime Ring Problem(DFS回溯)

    Prime Ring Problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  6. uva 193 Graph Coloring(图染色 dfs回溯)

    Description You are to write a program that tries to find an optimal coloring for a given graph. Col ...

  7. P1074 靶形数独 dfs回溯法

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶 ...

  8. 剪格子---(dfs回溯)

    如图p1.jpg所示,3 x 3 的格子中填写了一些整数. 我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60. 本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以 ...

  9. 蓝桥杯 算法提高 8皇后·改 -- DFS 回溯

      算法提高 8皇后·改   时间限制:1.0s   内存限制:256.0MB      问题描述 规则同8皇后问题,但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大. 输入格式 一个8*8 ...

  10. 46. Permutations 排列数

    46. Permutations 题目 Given a collection of distinct numbers, return all possible permutations. For ex ...

随机推荐

  1. iOS猜拳游戏源码

    利用核心动画和Quartz2D做的一个小游戏.逻辑十分简单. 源码下载:http://code.662p.com/<ignore_js_op> 详细说明:http://ios.662p.c ...

  2. Socket网络编程初探

    http://altboy.blog.51cto.com/5440160/1921720 客户端/服务器架构 即C/S架构,其实web服务在某种意义上也算是C/S架构 一个特点是服务器端持续运行对外提 ...

  3. Bug的分类和管理流程

    1.按照严重程度划分 定义:是指Bug对软件质量的破坏程度,即BUG的存在将对软件的功能和性能产生怎样的影响 分类:系统崩溃.严重.一般.次要.建议 2.按优先级划分 定义:表示处理和修正软件缺陷的现 ...

  4. Caused by: java.lang.IllegalArgumentException: Parameter Maps collection does not contain value for com.bj186.crm.mapper.UserMapper.Integer

    在使用SSM整合myBatis的过程中遇到了这个问题. 问题的原因: 把parameterType错误的写成了parameterMap 解决办法: 将parameterMap修改为parameterT ...

  5. 5 SQL 复杂查询

    5 复杂查询 5-1 视图 究竟视图是什么呢?如果用一句话概述的话,就是“从SQL的角度来看视图就是一张表”.实际上,在SQL语句中并不需要区分哪些是表,哪些是视图. 那么视图和表到底右什么不同呢?区 ...

  6. 利用system-config-kickstart实现半自动化安装

    老司机开车了… 上车请坐稳… centos7系统 首先确认已经安装了system-config-kickstart包,如果没有安装就yum install system-config-kickstar ...

  7. 一篇文章掌握nightwatch自动化测试

    nightwatch.js是一个web-ui自动化测试框架,被vue-cli深度整合进来.如果一个项目是基于vue-cli搭建的,基本可以做到开箱即用. 但是我们不可能一直都使用vue-cli.因为它 ...

  8. Educational Codeforces Round 50 (Rated for Div. 2)的A、B、C三题AC代码

    A题链接:https://codeforces.com/contest/1036/problem/A A题AC代码: #include <stdio.h> #include <std ...

  9. Nginx与python web服务配置(Uwsgi& FastCGI)

    Uwsgi start uswgi uwsgi --harakiri 360000 --body-read-warning=10000 --max-fd=65536 -b 1000000 --http ...

  10. python之tkinter变量设置 2014-4-9

    python 可以自己定义变量以及变量类型mystring = StringVar(ticked_yes = BooleanVoption1 = IntVar()volume = DoubleVar( ...