全排列总结

接触全排列已经好长时间了,一直没有抽空总结一下全排列的相关问题,下面来说一下!

排列

  一般地,从n个不同元素中取出mmn)个元素,按照一定的顺序排成一列,叫做从n个元素中取出m个元素的一个排列(Arrangement)。特别地,当m=n时,这个排列被称作全排列(Permutation)。

排列数公式:

特别,当n==m时为全排列的公式!

下一个全排列算法

  lintcode链接:http://www.lintcode.com/zh-cn/problem/next-permutation/

样例

  左边是原始排列,右边是对应的下一个排列。

  1,2,3 → 1,3,2

  3,2,1 → 1,2,3

  1,1,5 → 1,5,1

下一个全排列算法——first

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {//调用<algorithm>中的下一个排列算法以及排序算法if(!next_permutation(nums.begin(), nums.end()))sort(nums.begin(), nums.end(), less<int>());
}
}

下一个全排列算法——second

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {int ld = -1;if(nums.size() < 1) return ;for(int i=nums.size()-2; i>=0; --i)//找到从右边开始第一个比它右边相邻小的数的位置if(nums[i] < nums[i+1]){ld = i;break;}if(ld != -1){//找到从右边开始,第一个比位置为ld所在数 大的数的位置int rd;for(int i=nums.size()-1; i>=0; --i)if(nums[ld] < nums[i]){rd = i;break;}swap(nums[ld], nums[rd]);//在交换之前,ld位置后面的已经是从大到小排好序的,已经没有下一个排列了sort(nums.begin()+ld+1, nums.end());//交换之后,将ld后面的数置为最小的排列,从小到大排序
        }if(ld == -1){sort(nums.begin(), nums.end());}}
};

下一个全排列算法——third

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {        //挑战不使用额外的空间, 其实和方法二的思路是一样的int n = nums.size();for(int i=n-1; i>=0; --i)for(int j=n-1; j>i; --j)if(nums[i] < nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());return ;}sort(nums.begin(), nums.end());}
};

下一个全排列的思想:

 

  如上图所示,该全排列对应的下一个全排列是和绿色框里的数字以及红色框里的数字有关系的。显而易见的是红色框里的数列已经没有下一个排列了,因为已经是一个递减的序列了。为了得到下一个排列,我们将绿色框里的数字和红色框里右边起第一个比绿色框中数字大的数字交换位置(也就是4 和 5交换位置)。这样还不是下一个全排列,交换之后红色框内的序列为7 4 3 2 1 0, 将它变成递增序列 0 1 2 3 4 7,就得到了下一个全排列。

  因为,一个全排列,末尾的一段区间肯定是递减的(如上图的红色框区间),如果这段区间一直延伸到首部,那么也就没有下一个全排列了,否则找到和这段区间最近的一个数字(上图中绿色框中的数字),然后经过上述的处理就可以得到下一个全排列了。

  the second method 就是先找到绿色框数字的位置(ld), 然后在寻找红色框中右边第一个比绿色框中数字大的数字的位置(rd);

  the third method的意思就是从右边开始寻找第一对(i, j),满足nums[i]<nums[j], 对应second method中(ld, rd)。该方法没有用到额外的存储空间。

上一个全排列算法

  注:算法思想和下一个全排列的思想正好相反,步骤一致!

    lintcode链接:http://www.lintcode.com/zh-cn/problem/previous-permutation/

样例

  给出排列[1,3,2,3],其上一个排列是[1,2,3,3]

  给出排列[1,2,3,4],其上一个排列是[4,3,2,1]

上一个全排列算法——first

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {        //直接调用<algrotihm>中的算法prev_permutation()if(!prev_permutation(nums.begin(), nums.end()))sort(nums.begin(), nums.end(), greater<int>());return nums;          }
};

上一个全排列算法——second

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {      int ld = -1;if(nums.size() <= 1) return nums;for(int i=nums.size()-2; i>=0; --i)//找到从右边开始第一个比它右边相邻大的数的位置if(nums[i] > nums[i+1]){ld = i;break;}if(ld != -1){//找到从右边开始,第一个比位置为ld所在数 小的数的位置int rd;for(int i=nums.size()-1; i>=0; --i)if(nums[ld] > nums[i]){rd = i;break;}swap(nums[ld], nums[rd]);//在交换之前,ld位置后面的已经是从小到大排好序的,已经没有上一个排列了//交换之后,将ld后面的数置为最大的排列,从大到小排序sort(nums.begin()+ld+1, nums.end(), greater<int>());}if(ld == -1){sort(nums.begin(), nums.end(), greater<int>());}return nums;      }
};

上一个全排列算法——third

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {     int n = nums.size();for(int i=n-1; i>=0; --i)for(int j=n-1; j>i; --j)if(nums[i] > nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end(), greater<int>());return nums;}sort(nums.begin(), nums.end(), greater<int>());return nums;}
};

得到所有全排列算法

  lintcode链接:http://www.lintcode.com/zh-cn/problem/permutations/

全排列算法——first

  注:非递归,由下一个全排列算法——third方法实现

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());//方法1: 非递归实现int n = nums.size();bool flag = true;vv.push_back(nums);while(flag){flag = false;for(int i=n-1; i>=0; --i){for(int j=n-1; j>i; --j)if(nums[i] < nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());vv.push_back(nums);flag = true;break;}if(flag) break;}}       return vv;}
};

全排列算法——second

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        //方法2:调用<algorithm>中的next_permutation()do{vv.push_back(nums);}while(next_permutation(nums.begin(), nums.end()));            return vv;}
};

全排列算法——third

  注:递归思路:一共有nn个位置,然后每个位置枚举可能出现的数字(注意处理重复数字的情况)

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;    ///
    int nn;//没有 unique 之前的数组的大小map<int, int> mp;    void dfs_1(int cur, vector<int> &v, vector<int> nums){if(cur >= nn){vv.push_back(v);return;}for(int i=0; i<nums.size(); ++i)if(mp[nums[i]]){//如果nums[i]这个数字没有枚举完--mp[nums[i]];v.push_back(nums[i]);dfs_1(cur+1, v, nums);v.pop_back();++mp[nums[i]];}}    ////        vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        //方法3:递归for(int i=0; i<nums.size(); ++i)//统计每个重复元素的个数++mp[nums[i]];nn = nums.size();unique(nums.begin(), nums.end());//对数组进行去重vector<int> v;dfs_1(0, v, nums);       return vv;}
};

全排列算法——forth

  注:递归思路:每一个数,不断的和后面的数交换位置,每交换一次就会得到一个新的排列

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;void dfs_2(int ld, int rd, vector<int> nums){if(ld == rd){vv.push_back(nums);return ;}for(int i=ld; i<=rd; ++i){swap(nums[ld], nums[i]);dfs_2(ld+1, rd, nums);swap(nums[ld], nums[i]);}}vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        dfs_2(0, nums.size()-1, nums);return vv;}
};

排列序号(按字典序排序属于第几个全排列)

  注:不含重复数字的排列!

  lintcode链接:http://www.lintcode.com/zh-cn/problem/permutation-index/

样例

  例如,排列[1,4,2]是第2个全排列。

排列序号算法

  算法思想请参考:http://www.cnblogs.com/hujunzheng/p/5020211.html

class Solution {
public:/*** @param A an integer array* @return a long integer*/long long permutationIndex(vector<int>& A) {//一个一个来肯定会超时// vector<int> permu(A.begin(), A.end());// sort(permu.begin(), permu.end());// int cnt = 0;// do{//     int i;//     for(i=0; i<A.size(); ++i)//         if(A[i]!=permu[i])//             break;//     ++cnt;//     if(i>=A.size()) break;// }while(next_permutation(permu.begin(), permu.end()));// return cnt;
                vector<int> a;int len = A.size();int cnt[len];cnt[len-1] = 0;a.push_back(A[len-1]);for(int i=len-2; i>=0; --i){//统计每个数后面有多少个比它小的数的个数vector<int>::iterator it = lower_bound(a.begin(), a.end(), A[i]);cnt[i] = it-a.begin();a.insert(it, A[i]);}long long ans=1, fac=1, c=1;for(int i=len-2; i>=0; --i)ans += (fac*=c++)*cnt[i];return ans;}
};

第k个排列

  注:给定 n 和 k,求123..n组成的排列中的第 k 个排列。

  lintcode链接:http://www.lintcode.com/zh-cn/problem/permutation-sequence/

样例

  对于 n = 3, 所有的排列是:123, 132, 213, 231, 312, 321.

  如果 k = 4, 第4个排列为,231.

康托展开的公式 :

  X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!,ai为整数,并且0<=ai<i(1<=i<=n)

  适用范围:没有重复元素的全排列

例题:

  找出第16个n = 5的序列(12345)。康托展开只要O(n)就行 ,下面来说说具体怎么做:

  根据第一行的那个全排列公式,15 / 4! = 0 …15  =>  有0个数比它小的数是1,所以第一位是1

  拿走刚才的余数15,用15 / 3! = 2 …3   =>  剩下的数里有两个数比它小的是4(1已经没了),所以第二位是4

  拿走余数3, 用 3 / 2! = 1 …1   =>  剩下的数里有一个数比它小的是3,所以第三位是3

  拿走余数1, 用 1/  1! = 1 …0    =>  剩下的数里有一个数比它小的是 5(只剩2和5了),所以第四位是5

  所以排列是 1,4,3,5,2

第k个排列算法

 

class Solution {
public:/*** @param n: n* @param k: the kth permutation* @return: return the k-th permutation*/string getPermutation(int n, int k) {if(n==1) return "1";int f[n+1];bool use[n+1];f[1] = 0;f[2] = 1;memset(use, false, sizeof(use));for(int i=3; i<=n; ++i)f[i] = f[i-1]*(i-1);string ans = "";--k;//要计算的排列之前有多少个排列for(int i=n; i>=2; --i){int cnt = 0;int c = k/f[i];//假设该排列的这位数是x,c就是比x小的数(之前没有用过)的个数k%=f[i];for(int j=1; j<=n; ++j){//寻找符合要求的x的值if(!use[j]){if(cnt == c){ans += j+'0';use[j] = true;break;}++cnt;}}}for(int j=1; j<=n; ++j)if(!use[j]){ans += j+'0';break;}return ans;}
};

 

带重复元素的排列

  题目链接:http://www.lintcode.com/zh-cn/problem/permutations-ii/

带重复元素的排列——first

  思路:next_permutation()本身支持带重复元素的全排列

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());do{ans.push_back(nums);}while(next_permutation(nums.begin(), nums.end()));

return ans;} };

 

带重复元素的排列——second

  思路:枚举每个位置肯能出现的数字。

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;map<int, int> cnt;//记录每个数字在nums中出现的次数 void dfs(int n, vector<int> &nums, vector<int> &v){if(v.size() >= n){ans.push_back(v);return ;}for(int i=0; i<nums.size(); ++i)if(cnt[nums[i]]){--cnt[nums[i]];v.push_back(nums[i]);dfs(n, nums, v);v.pop_back();++cnt[nums[i]];}}vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());for(int i=0; i<nums.size(); ++i)++cnt[nums[i]];int n = nums.size();nums.erase(unique(nums.begin(), nums.end()), nums.end());//清除重复的元素vector<int> v;dfs(n, nums, v);return ans;}
};

 

带重复元素的排列——third

  思路:和 “全排列算法——first”方法类似,唯一不同的是下面代码中红色部分

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());       bool flag = true;while(flag){flag = false;ans.push_back(nums);for(int i=nums.size()-1; i>=0; --i){for(int j=nums.size()-1; j>i; --j)if(nums[i] < nums[j]){flag = true;while(j-1>i && nums[j-1]==nums[j]) --j;//如果前面有相同的数字,找到最左边的数字
                        swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());break;}if(flag) break;}}return ans;}
};

 

转载于:https://www.cnblogs.com/hujunzheng/p/5037121.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大小端问题傻傻分不清?

先来熟悉一下概念&#xff1a; 大端&#xff1a;数据的高位数据保存在低位地址&#xff0c;数据的低位数据保存在高地址 小端&#xff1a;数据的高位数据保存在高位地址&#xff0c;数据的低位数据保存在低地址为什么会存在大小端的问题&#xff1f; 这是因为在计算机系统中&a…

mount --bind的用处

&#xff08;一&#xff09;mount --bind介绍 mount --bind的作用是将两个目录连接起来&#xff0c;例如&#xff1a;mount ---bind /dir1 /dir2 是将dir1目录挂载到dir2目录上&#xff0c;下面来实际演示一下&#xff1a; 上面的操作中首先创建了dir1 dir2两个目录&#xf…

lintcode 落单的数(位操作)

题目1 落单的数 给出2*n 1 个的数字&#xff0c;除其中一个数字之外其他每个数字均出现两次&#xff0c;找到这个数字。 链接&#xff1a;http://www.lintcode.com/zh-cn/problem/single-number/ 样例 给出 [1,2,2,1,3,4,3]&#xff0c;返回 4 挑战 一次遍历&#xff0c;常数级…

旋转图像

旋转图像 给定一个NN的二维矩阵表示图像&#xff0c;90度顺时针旋转图像。 看个例子 算法1&#xff1a; 如上图所示&#xff0c;设一个N阶二维矩阵&#xff0c;则将矩阵从外向里可以分成N/2个圈&#xff0c;例如&#xff08;1 2 3 4 8 12 16 15 14 13 9 5&#xff09;这是最外边…

嵌入式开发板模拟器:QEMU

前两天看微信公众号时发现了一个嵌入式模拟器&#xff0c;感觉很不错&#xff0c;自己动手安装了一个&#xff0c;折腾了几天&#xff0c;下载一直是个问题&#xff0c;特此记录如下 模拟器大家应该都听说过&#xff0c;有的小伙伴打游戏也会安装模拟器&#xff0c;今天我们介绍…

gcc: weak_alias如何使用

本文主要说明weak和alias是什么和如何使用它 __attribute__是用来说明函数的属性&#xff0c;weak和alias分别是两个属性。 &#xff08;一&#xff09;强符号和弱符号&#xff1a; 强符号&#xff1a;已经初始化的全局变量和未被weak修饰的函数弱符号&#xff1a;未初始化的全…

静态Include和动态Include测试并总结

主要代码 hjzgg.css .center-div{width:auto;margin-left: 40%;margin-right: 40%;display: block;position: absolute;top:0px;left:0px; }.text-div{margin-top: 80px; }.hjzgg-div{color:transparent;font-size:20px;font-weight: bold;letter-spacing:2px;-webkit-animatio…

ueditor的配置和使用

ueditor下载好之后直接复制到项目的WebContent目录下&#xff0c;并将ueditor\jsp\lib下的jar包复制或者剪切到项目的lib目录下。先看一下效果&#xff0c;如下&#xff1a; 1.文件的上传 首先在ueditor/jsp目录下找到config.json文件&#xff0c;就拿Image上传来说吧。 "…

windows上搭建NFS服务器

在进行嵌入式开发的时候&#xff0c;我们常用的做法是搭建NFS服务器&#xff0c;然后使把文件系统、调试程序放在NFS服务器上&#xff0c;这样可以方便调试&#xff0c;以前都是在linux里面开启NFS服务器&#xff0c;今天来说下window里的nfs服务器–haneWin 一、软件安装和使…

Hibernate注解

前言&#xff1a; 最近正在学习Hibernate通过注解&#xff08;annotation&#xff09;来管理映射关系&#xff0c;以前都是通过XML映射文件。下面拿个小例子说一下。 数据库物理模型&#xff1a; 数据库的描述&#xff1a; 一篇博客随笔可以分到不同的类中&#xff0c;一个类中…

zyUpload+struct2完成文件上传

前言&#xff1a; 最近在写自己的博客网站&#xff0c;算是强化一下自己对s2sh框架的理解。期间遇到了很多问题&#xff0c;这些问题在写之前都考虑过&#xff0c;感觉也就是那样吧。但正真遇到了&#xff0c;也挺让人难受的。就利用zyUpload这个js插件实现文件的上传&#xff…

java发送内嵌图片邮件

前言&#xff1a; 博客系统中需要邮件服务的功能&#xff0c;以前写过类似的功能&#xff0c;不过功能太简单了&#xff0c;仅仅是发送文本内容&#xff0c;现在尝试一下发送内嵌图片邮件&#xff01; 准备工作&#xff1a; 请参考&#xff1a;http://www.cnblogs.com/hujunzhe…

SD卡移植FAT32文件系统无MBR

问题&#xff1a;在研究SD卡和FAT32文件系统的时候&#xff0c;发现SD卡有的有MBR&#xff0c;有的没有MBR&#xff0c;这个为什么呢&#xff1f; 分析&#xff1a;MBR是主引导记录&#xff0c;是在给磁盘分区的时候建立的&#xff0c;我们的SD卡没有这个可能就是没有进行过分区…

FAT32文件系统介绍

FAT32文件系统&#xff08;一&#xff09;为什么要有文件系统&#xff08;二&#xff09;FAT32文件系统组成&#xff08;三&#xff09;分步介绍各部分(1) 首先介绍一下MBR(2)DBR介绍(3)FAT表介绍(4) 数据区&#xff08;一&#xff09;为什么要有文件系统 文件系统是操作系统用…

java中动态代理实现机制

前言&#xff1a; 代理模式是常用的java设计模式&#xff0c;它的特征是代理类与委托类有同样的接口&#xff0c;代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类&#xff0c;以及事后处理消息等。代理类与委托类之间通常会存在关联关系&#xff0c;一个代理类…

libiconv库简单裁剪支持CP437编码

有许多人在做项目的时候都会遇到字符编码的不一致导致的乱码问题&#xff0c;那如何去解决呢&#xff1f;在Linux系统上可以通过iconv函数族来进行编码转换&#xff0c;但有时候我们并不需要全部的字符集&#xff0c;因为可能会占用比较大的空间&#xff0c;本文主要支持CP437编…

简单java在线测评程序

简单java程序在线测评程序 一&#xff0e;前言 大家过年好&#xff01;今年的第一篇博客啊&#xff01;家里没有网&#xff0c;到处蹭无线&#xff01;日子过得真纠结&#xff01;因为毕设的需求&#xff0c;简单写了一个java程序在线测评程序&#xff0c;当然也可以在本地测试…

简单文本编辑器

一、前言 聚天地之灵气&#xff0c;集日月之精华&#xff01;一个简单的java文本编辑器由此而生。毕设所需&#xff0c;很是无奈&#xff01; 二、界面预览 三、实现思路 1.字体选择器的实现 (1).字体类 class MyFont{private Font font;private Color color;public Font getFo…

u-boot新增命令后出现data abort

&#xff08;一&#xff09;问题描述 u-boot下新增了一条update的命令&#xff0c;直接输入update没有报错&#xff0c;但是输入up按TAB键补全时发现出现data abort&#xff0c;而且输入不支持的命令也会有data abort &#xff08;二&#xff09;解决方法 最开始我包含的头…

core文件如何分析

目录(一&#xff09;什么是coredump(二)coredump产生的条件&#xff08;1&#xff09;coredump产生主要原因&#xff1a;&#xff08;2&#xff09;如何生成coredump(三&#xff09;gdb使用(四&#xff09;实例调试coredump文件(五&#xff09;总结(一&#xff09;什么是coredu…