全排列邻位对换法c语言算法,全排列及相关扩展算法(六)——全排列最蛋疼的算法:邻位对换法...
1.引入原因:在此之前我们实现全排列本质上都是采用单向交换的思路,当交换到末端便要回溯至上一层面,如果我们采用双向的交换,便可以不断地交换下去,于是产生了邻位对换法。邻位对换法在找下一个排列的方法上在很多情况下要比字典序算法要快上许多,因为每次的下一个排列只是交换两个相邻的元素,当然缺点就是到左端或者右端时要进行找最大可移动数的计算,故最终整体效率也没什么提升,所以称之为最蛋疼的全排列算法。
2.定义:
定义1:在一个序列中任意两个元素对如果满足a[k] < a[m](k < m),则称为正序,否则称为逆序。
定义2:如果一个排列中逆序的数目为奇数,则称为奇排列,若为偶数则称为偶排列。
初始的时候,我们假设这个序列是一个升序的序列,而且最小元素至少为1,升序的序列总是偶排列,并且我们设定初始所有数的移动(其实就是交换)的方向均从右向左。我们给出可移动的概念:如果一个数沿着它的方向的邻位比它小,则称这个数是可移动的。由这个概念可以知道,1是永远不可移动的,最大的数除非是在两端而且方向指向序列外面,要不一直是可移动的。我们再规定一个性质:如果一个可移动的数发生了移动,那么比它大的所有数的方向都反过来。对于一个排列而言,它的邻位对换法的下一个排列是最大的可移动的数移动后的结果。
以{1,2,3,4}为例:
1,2,3,4.[很显然,这是一个偶排列,因为它是升序序列。1<2,2<3,3<4,都是正序]。为了得到下一个排列,我们取最大的数4,它是最大的可移动数,并把它向左移动:
1,2,4,3.实际上是3和4的交换,这便是它的下一个排列。再移动:
1,4,2,3.再移动,
4,1,2,3.
由此我们得到了四个排列,每次都是通过交换相邻元素实现的。当4到头了之后,无法移动了,此时我们找到可移动的最大的数3,并把它向左移动1次,得到
4,1,3,2.
此时由于4的移动方向已经反过来了,所以最大可移动数为4,把4依次向右移动:
1,4,3,2
1,3,4,2
1,3,2,4.
4到了头,再次无法移动了,此时最大的可移动的数变成了3,把3向左移动一次,得到
3,1,2,4.
此时4的移动方向再反过来向左,得到
3,1,4,2.
3,4,1,2.
4,3,1,2.
此时3也到头了,此时我们找可移动的最大的数,2.得到
4,3,2,1.
4和3的移动方向再反向右,
3,4,2,1
3,2,4,1
3,2,1,4.
4到头后,由于此时3的移动方向向右,得到
2,3,1,4.
则4的方向又反,向左移动
2,3,4,1
2,4,3,1.
4,2,3,1.
此时3再向右移动,得到
4,2,1,3.
此时4再向右移动
2,4,1,3.
2,1,4,3.
2,1,3,4.
由此,我们从一个升序排列得到了全排列。
3.总结:
邻位对换法的过程:
1.找到最大可移动数并移动至端点
2.找到现存的最大可移动数移动一次
3.回到原最大可移动数并移动至另一端点
4.找到现存的最大可移动数移动一次
......
5.找不到最大可移动数,循环结束,遍历结束。
邻位对换法的下一个排列:
首先找到最大数的位置,然后判断方向。每当我们的最大的数从一端移到另一端时,就要进行最大可移动数的交换,这个过程过后,在不考虑最大数的数列中便会增加一个逆序。而初始的逆序为0,最大的数在移动的过程中不会改变除去最大数后的数列的顺序,所以,不考虑最大数后的数列的逆序为偶数个时(偶排列),最大数在向左移动,逆序为奇数个时(奇排列),最大数向右移动。同样地,如果最大数此时在某一端点,我们可以通过上述性质判断是移动最大数还是找到最大可移动数并进行交换。非最大数方向也是如此:由所有比它小的数构成的序列的逆序决定的,如果是偶数个时,向左,奇数个时向右。
邻位对换法的中介数:
对于某个排列如2341,从1开始看起(1没有方向,不用算),如果它是向左的,则右边,所有比它小的数的个数为中介数的最高位,如果是向右的,则左边所有比它小的数的个数为中介数的最高位,位逐次降低,如2对应k[1] = 1,最高位为1,3对应k[2] = 1,第三位为1,4是向左的,第二位为k[3] = 1。得到111为2341的中介数。
其对应的序号的求法为:
(1 * 3 + 1) * 4 + 2 = 17,和递减进位的公式一样,所以通过序号求中介数也是和递减进位、递增进位类似,除以n取余,除以n-1除余,。。。。。。
邻位对换法由中介数求原排列的方法:
首先要判断最大数的方向,这个和上面一样,要由其它数构成的序列的奇偶性决定,所以要求其它数构成序列的奇偶性,这个奇偶性通过求序号的公式来得到,由性质原排列中取所有小于等于k的数构成的数列(顺序不变)的奇偶性和对应的序号的奇偶性相同。我举简单的例子:2341的中介数为112.那么231的中介数为11(舍掉低相应位数即可得到相应的中介数),则它的序号为1*3+1 = 4是偶数,所以最高位的方向4的方向向左。以此类推即可。
4.算法代码:bool Movable(int A[], bool direct[], int n) //direct参数用于接收每个元素移动方向的数组。
{
int max = 1;//初始化最大可移动数为1,因为规定1是最小的数,可以自己设定。
int pos = -1;//初始化最大可移动数的位置为-1.
/*下面先找到最大可移动数位置*/
for (int i = 0; i
{
if (A[i]
continue;
if ((i A[i + 1] && direct[i]) || (i> 0 && A[i] >A[i - 1] && !direct[i]))
{
max = A[i];
pos = i;
}
}
/*下面对它进行移动*/
if (pos == -1)
return false;
if (direct[pos])
{
swap(A[pos], A[pos + 1]);
swap(direct[pos], direct[pos + 1]);
}
else
{
swap(A[pos], A[pos - 1]);
swap(direct[pos], direct[pos - 1]);
}
/*最后调整所有比最大可移动数大的数的方向*/
for (int i = 0; i
{
if (A[i] > max)
direct[i] = !direct[i];
}
return true;
}
void Full_Array(int A[], int n)
{
bool* direct = new bool[n]; //产生一个记录每个元素移动方向的数组
sort(A, A + n); //将原序列变成一个升序
for (int i = 0; i
direct[i] = false;//初始化移动方向为false,表示从右向左。
do
{
Print(A, n);
if (A[n - 1] == n)
for (int i = n - 1; i>0; i--)
{
swap(A[i], A[i - 1]);
swap(direct[i], direct[i - 1]);
Print(A, n);
}
else
for (int i = 0; i
{
swap(A[i], A[i + 1]);
swap(direct[i], direct[i + 1]);
Print(A, n);
}
} while (Movable(A, direct, n));
delete[]direct;
}
5.运行截图:
6.几种全排列算法效率对比:
递增(递减)通过中介数增长逆推全排列的效率就明显不如其他几种,就不参与对比了。
这里对比了字典序法、对比邻位对换法、普通回溯递归法以及字典序法STL模版next_permutation算法。
数据选择了{ 1,2,3,4,5,6,7,8,9,10,11,12},毕竟是阶乘太大了时间会很久,有兴趣的可以多加几位试试
运行截图:
对于模版……我可能用了假的
注:用Count计数来保证函数执行的正确性,用 while (next_permutation(A, n))这种方法的话注意保证输入数据是递增的,且运行数会比其他的少1,因为不会计算第一组数据。
7.参考文档
全排列邻位对换法c语言算法,全排列及相关扩展算法(六)——全排列最蛋疼的算法:邻位对换法...相关推荐
- 全排列及相关扩展算法(六)——全排列最蛋疼的算法:邻位对换法
1.引入原因:在此之前我们实现全排列本质上都是采用单向交换的思路,当交换到末端便要回溯至上一层面,如果我们采用双向的交换,便可以不断地交换下去,于是产生了邻位对换法.邻位对换法在找下一个排列的方法上在 ...
- Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)
Algorithm:C++语言实现之字符串相关算法(字符串的循环左移.字符串的全排列.带有同个字符的全排列.串匹配问题的BF算法和KMP算法) 目录 一.字符串的算法 1.字符串的循环左移 2.字符串 ...
- c语言如何实现1-n全排列,全排列思路解析附C语言实现
全排列这个问题,几乎是面试必问的问题,考察面试者递归,逻辑分析问题的能力 笔者在10年看谭浩强的c语言的时候,就接触过这个算法,只可惜那时候老师并没有严格让我们去实现 自己学习的时候也偏注重语法的理解 ...
- 算法学习——求有重复元素的全排列(递归)
算法学习--求有重复元素的全排列(递归) 思路:看到这个题目首先能想到的一点就是:①我们要求元素的所有全排列②我们要对求出的全排列去重 第一步:求全排列,这里先讨论对不含重复元素的数组元素进行全排列, ...
- c语言实验报告 折半查找法,C语言数组之冒泡排序+折半查找法(二分查找)
冒泡排序算法 将相邻的元素进行两两比较,大的向后"冒", 小的向前"赶". 口诀: N个数字来排队,两两比较小靠前 外层循环N-1(控制需要比较的轮数). 内层 ...
- 常用查找法(C语言)
常用查找法(C语言) 顺序查找法 原理: 顺序查找是非常简单常用的查找算法,基本思路:从第一个元素m开始逐个与需要查找的元素x进行比较,当比较到元素值相同(即m=x)时返回元素m的下标,如果比较到最后 ...
- R语言时间序列(time series)分析实战:霍尔特指数Holt‘s平滑法预测
R语言时间序列(time series)分析实战:霍尔特指数Holt's平滑法预测 目录
- Algorithm:C+语言实现之数组相关算法(和为定值的两个数、和为定值的m个数、荷兰国旗、长度为2n的洗牌算法、任意长度数组的洗牌算法)
Algorithm:C+语言实现之数组相关算法(和为定值的两个数.和为定值的m个数.荷兰国旗.长度为2n的洗牌算法.任意长度数组的洗牌算法) 目录 数组 1.寻找和为定值的两个数 2.和为定值的m个数 ...
- Algorithm:C++语言实现之队列相关算法(最短路径条数问题、拓扑排序)
Algorithm:C++语言实现之队列相关算法(最短路径条数问题.拓扑排序) 目录 队列 1.最短路径条数问题 2.拓扑排序 队列 1.最短路径条数问题
最新文章
- 第十六讲 循环遍历文件和元组
- 详解wait和waitpid函数
- Hadoop HBase概念学习系列之HRegion服务器(三)
- Exchange 2016异地容灾系列-Exchange部署(五)
- 人工智能TensorFlow工作笔记010---TensorFlow 游乐场游戏,了解神经网络主要功能作用_工作流程
- 必须包含数字和字母,字符随意的正则表达式
- linux环境下redis安装
- 百度首页被tn劫持的办法有那些、两种解决百度劫持的方法
- CHM提示无法连接Internat
- 商业银行vh是哪个银行的简称_各个银行的简称是什么?
- 乔布斯其人的演讲技巧
- jQuery.closest() 函数详解
- 隐私保护联邦学习之差分隐私原理
- NSFC 申请不中的反思 (内部讨论)
- Java多线程 第三章 等待/通知(wait/notify)机制
- nacos使用mysql8作为存储媒介时报Caused by: com.mysql.cj.exceptions.CJException: Public Key Retrieval is not all
- Arm芯片上电启动流程剖解
- 微信小程序 云开发表数据一键清空
- 基于svg绘制北京地铁图(官网数据来源)
- CSS的再深入2(更新中···)