组合排列。。。初中的知识用到计算机上也这么麻烦。。
来源:互联网 发布:免费下载漫画的软件 编辑:程序博客网 时间:2024/04/29 20:25
编程很多时候用到排列组合。。。
组合可以搜索解决,也可以位运算轻松解决。
比如有len个数字num[]保存,求出所有组合。。。
那么就可以这样写
for (int i = 1; i < (1 << len); ++ i)// get zuhe{int len1 = 0;for (int j = 0; j < len; ++ j){if (i & (1 << j)){zuhe[len1 ++] = num[j];}}}
实际上就是有那个数那么就有n个对应的位,1 1 1 1 1。。。。这些位可以为0或者1两种情况,那么从1 到2^n-1不正是代表了所有种情况吗?然后在求为1位提出来就okle。
下面说一下全排列:
首先C++ STL直接调用next_permutation,注意这个函数是有顺序的,从小到大,比如123,如果初始化为321,那么下一个就没了,已经到最后了。如果你想要获得所有的,那么初始化必须是123.。。。
用法就是zuhe[] ; 数组长度len1,初始化好序列。。。
do {输出zuhe。。。。
} while (next_permutation(zuhe, zuhe + len1));//qu pai lie
全排列递归算法:
所谓全排列,就是将集合中元素的所有排列情况依次输出。比如{1、2、3}的全排列为:123、132、213、231、312、321,共6种,满足计算公式N!(N为集合中元素个数,不重复)。
当元素不重复时,全排列采用递归思想较容易实现,它的递归公式推导步骤类似:
1、要求得123的全排列,只需求得:1并上23的全排列(1 23, 1 32),2并上13的全排列(2 13, 2 31),3并上12的全排列(3 12 321)。
2、对于23的全排列,只需求得2并上3的全排列,3并上2的全排列。步骤1中13、12的全排列也类似。
3、对于3的全排列或者2的全排列,就是它们的本身。递归结束。
递归实现不重复元素全排列算法的实现代码(C++)如下:
//交换a和bvoid Swap(int *a, int *b){ int t = *a; *a = *b; *b = t;} //全排列函数。list:待排元素列表,start:起始位置下标,end:最后一个有效元素的下一个下标。void Permutation(int start, int end, int list[]){ int i; if (start >= end) //递归结束,打印当前这次全排列结果,返回。 { for (i = 0; i < end; i++) { printf("%d ", list[i]); } printf("\n"); return; } //对于给定的list[start...end],要使区间中每一个元素都有放在第一位的机会, //然后开始递归调用自身,得到list[start+1...end]的全排列。 for (i = start; i < end; i++) { Swap(&list[i], &list[start]); //交换元素,使每一个元素都有放在第一位的机会。 Permutation(start+1, end, list); //递归调用 Swap(&list[i], &list[start]); //恢复原始的list,不影响下次递归调用。 }}
上述程序的调用方法为:
#include <iostream>using namespace std; int main(){ int a[] = {1, 2, 3}; Permutation(0, 2, a); return 0;}
STL的实现方法:
当待排元素列表含有重复项时,上述算法就需要改进,其中一种方法可以是维护一个存放不重复排列的集合,每次新生成1个排列,如果集合中不存在这个排列,则插入排列,否则,放弃。
要实现含重复元素的全排列算法,可以参考STL中next_premutation()函数的实现方法(在algorithm.h中声明)。该函数会将列表中元素按字典序(wiki)给出全排列中的下一个排列,它的实现算法为:
令当前排列为P(0)P(1)P(2)...P(n-1)P(n)。则求它下一个排列的过程为,
1、从后往前遍历,找到第一个P(i)>P(i-1)的元素,记录下标i。比如排列1、5、2、4、3中满足条件的元素为4,记下它的下标i = 3,因为P(i)是4,P(i-1)是2,满足P(i)>P(i-1)。如果找不到这样的i,则表示该序列已经是字典序中的最后一个序列,结束算法。
2、从后往前遍历,找到第一个P(j)>P(i-1)的数,记录下标k。还是上面这个例子,P(i-1)为2,从后往前第一个大于P(i-1)是P(4)=3,因此记录下j=4。
3、互换P(i-1)和P(j),得到新序列1、5、3、4、2。
4、将P[i...n]间的元素逆置,返回序列。上述例子中为逆置4和2,得到最终的序列1、5、3、2、4。
用比较通俗的例子解释一下上述步骤:
假设现在有一个序列4、6、5、3、2、1,要求得字典序的下一个序列。首先,从后往前找到第一个i,使得P(i)>P(i-1),明显这里i是1,P(i)=6,这个意思是,在6之后的元素,都是按值递减的,否则第一步求i的时候也不会找到第2个元素6才满足条件。现在知道,从i开始到最后,其实是字典序里的最大序列了(一直按值递减)。第二步,拿出i的前一个元素P(i-1)=4,将它与原序列从后往前第一个大于它的元素交换位置,这里这个与4交换的元素是5,这样序列就变成了5、6、4、3、2、1,至此,最高位升了一级(4->5),接着要把低位的从最大变成最小(就像199之后是200,最高为从1变成2后,要把低位从最大99变成最小00),这里的低位是最大序列6、4、3、2、1,变成最小序列只需逆置即可,变成1、2、3、4、6,原序列变为5、1、2、3、4、6,即为所求。
实现代码如下:
/** *如果存在当前序列在字典序中的下一个排列,则返回true, *否则返回false。 */bool next_premutation(int list[], int length){ int i, j; //步骤1:得到i。 for (i = length - 1; i > 0; i--) { if (list[i] > list[i-1]) { break; //记下下标i。 } } if (i <= 0) { //表示当前排列已经是字典序中的最后一个序列,没有下一个了。 return false; } //步骤2:得到j。 for (j = length - 1; j > 0 ; j--) { if (list[j] > list[i-1]) { break; //记下下标j。 } } //步骤3:互换list[i-1]和list[j]。 int temp = list[i-1]; list[i-1] = list[j]; list[j] = temp; //步骤4:逆置list[i...n]。 int start, end; for (start = i, end = length-1; start < end; start++, end--) { int temp = list[start]; list[start] = list[end]; list[end] = temp; } return true;}
采用这种方法要获得一个集合的全排列,可按下面方法调用(和stl函数next_permutation()的调用方法基本一致):
#include <iostream>
using namespace std;
int main()
{
int list[] = {1, 2, 3, 4, 5};
do
{
for (int i = 0; i < 5; i++)
{
printf("%d ", list[i]);
}
printf("\n");
}while (next_premutation(list, 5));
return 0;
}
- 组合排列。。。初中的知识用到计算机上也这么麻烦。。
- 原来冰箱也这么麻烦
- 这么麻烦的网站
- 排列、组合相关知识
- DayOfWeek的转换需要这么麻烦吗
- DayOfWeek的转换需要这么麻烦吗?
- 为什么 SeaJS 模块的合并这么麻烦
- 哲学意思上的计算机知识体系结构
- C语言实现数学上的组合和排列
- 呵呵,小偷也有麻烦的时候!
- MFC 树控件,也挺麻烦的
- 计算器用到的知识
- 字符串的排列/组合
- 字符串的排列、组合
- 字符串的组合排列
- 本人系初中教师,爱好计算机,所以业余制作了一些小东西,渴望与大家分享,也希望得到大家的指导!
- Gcc上遇到的麻烦!!
- 这么麻烦呀
- IPV6正则表达式
- Android 安全机制概述
- Android 安全机制概述
- Ubuntu系统安装系列
- 使用HOG+libsvm
- 组合排列。。。初中的知识用到计算机上也这么麻烦。。
- Android学习笔记————Content Provider
- java多线程面试题
- QT中播放FLASH的问题
- MST性质的证明
- Android学习笔记————Service
- Assert宏的一些理解
- Android学习笔记----Activity
- 已知三角形三点坐标,求三角形的面积(二维和三维)