算法之排列算法与组合算法详解

 更新时间:2020年4月25日 17:41  点击:1453

1. 前言

本文介绍了常用的排列组合算法,包括全排列算法,全组合算法,m个数选n个组合算法等。

2. 排列算法

常见的排列算法有:
(A)字典序法
(B)递增进位制数法
(C)递减进位制数法
(D)邻位对换法
(E)递归法

介绍常用的两种:

(1) 字典序法

对给定的字符集中的字符规定了一个先后关系,在此基础上按照顺序依次产生每个排列。

[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。

生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有字典顺序中相邻的字符串。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

算法思想:

设P是[1,n]的一个全排列。
P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn , j=max{i|Pi<Pi+1}, k=max{i|Pi>Pj} ,对换Pj,Pk,将Pj+1…Pk-1PjPk+1…Pn翻转, P'= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个

例子:839647521的下一个排列.

从最右开始,找到第一个比右边小的数字4(因为4<7,而7>5>2>1),再从最右开始,找到4右边比4大的数字5(因为4>2>1而4<5),交换4、5,此时5右边为7421,倒置为1247,即得下一个排列:839651247.用此方法写出全排列的非递归算法如下

该方法支持数据重复,且在C++ STL中被采用。

(2) 递归法

设一组数p = {r1, r2, r3, … ,rn}, 全排列为perm(p),pn = p – {rn}。则perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), … , rnperm(pn)。当n = 1时perm(p} = r1。

如:求{1, 2, 3, 4, 5}的全排列

1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。

由于一个数的全排列就是其本身,从而得到以上结果。

2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。

即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.

#include <stdio.h>
 
int n = 0;
 
void swap(int *a, int *b)
 
{
 
 int m;
 
 m = *a;
 
 *a = *b;
 
 *b = m;
 
}
 
void perm(int list[], int k, int m)
 
{
 
 int i;
 
 if(k > m)
 
 {
 
  for(i = 0; i <= m; i++)
 
   printf("%d ", list[i]);
 
  printf("\n");
 
  n++;
 
 }
 
 else
 
 {
 
  for(i = k; i <= m; i++)
 
  {
 
   swap(&list[k], &list[i]);
 
   perm(list, k + 1, m);
 
   swap(&list[k], &list[i]);
 
  }
 
 }
 
}
 
int main()
 
{
 
 int list[] = {1, 2, 3, 4, 5};
 
 perm(list, 0, 4);
 
 printf("total:%d\n", n);
 
 return 0;
 
}

3. 组合算法

3.1 全组合

在此介绍二进制转化法,即,将每个组合与一个二进制数对应起来,枚举二进制的同时,枚举每个组合。如字符串:abcde

00000 <– –> null
00001<– –> e
00010 <– –> d
… …
11111 <– –> abcde

3.2 从n中选m个数

(1) 递归

a. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。

b. 从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。

下面是递归方法的实现:

/// 求从数组a[1..n]中任选m个元素的所有组合。
 
/// a[1..n]表示候选集,n为候选集大小,n>=m>0。
 
/// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
 
/// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。
 
void combine( int a[], int n, int m, int b[], const int M )
 
{
 
 for(int i=n; i>=m; i--)  // 注意这里的循环范围
 
 {
 
  b[m-1] = i - 1;
 
  if (m > 1)
 
   combine(a,i-1,m-1,b,M);
 
  else           // m == 1, 输出一个组合
 
  {
 
   for(int j=M-1; j>=0; j--)
 
   cout << a[b[j]] << " ";
 
   cout << endl;
 
  }
 
 }
 
}

(2) 01转换法

本程序的思路是开一个数组,其下标表示1到n个数,数组元素的值为1表示其代表的数被选中,为0则没选中。

首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。

然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。

当第一个“1”移动到数组的n-m的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。

例如求5中选3的组合:

1 1 1 0 0 //1,2,3
 
1 1 0 1 0 //1,2,4
 
1 0 1 1 0 //1,3,4
 
0 1 1 1 0 //2,3,4
 
1 1 0 0 1 //1,2,5
 
1 0 1 0 1 //1,3,5
 
0 1 1 0 1 //2,3,5
 
1 0 0 1 1 //1,4,5
 
0 1 0 1 1 //2,4,5
 
0 0 1 1 1 //3,4,5

4. 参考资料
(1) //www.jb51.net/article/54441.htm
(2) //www.jb51.net/article/54443.htm
(3) 组合算法

本程序的思路是开一个数组,其下标表示1到m个数,数组元素的值为1表示其下标代表的数被选中,为0则没选中。

首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。

然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为 “01”组合,同时将其左边的所有“1”全部移动到数组的最左端。

当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得 到了最后一个组合。

例如求5中选3的组合:
1 1 1 0 0 //1,2,3
1 1 0 1 0 //1,2,4
1 0 1 1 0 //1,3,4
0 1 1 1 0 //2,3,4
1 1 0 0 1 //1,2,5
1 0 1 0 1 //1,3,5
0 1 1 0 1 //2,3,5
1 0 0 1 1 //1,4,5
0 1 0 1 1 //2,4,5
0 0 1 1 1 //3,4,5 

[!--infotagslink--]

相关文章

  • C#几种排序算法

    作者:Sabine 【导读】本文介绍了C#的四种排序算法:冒泡排序、选择排序、插入排序和希尔排序  冒泡排序 using System; namespace BubbleSorter { public class Bubb...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • 图文详解Heap Sort堆排序算法及JavaScript的代码实现

    这篇文章以图文详解Heap Sort堆排序算法及JavaScript的代码实现,堆排序算法基于类二叉树的堆数据结构,需要的朋友可以参考下...2016-05-05
  • 同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

    这篇文章主要给大家介绍了关于同时兼容JS和C#的RSA加密解密算法,通过该算法可以对web提交的数据进行加密传输,文中通过图文及示例代码介绍的非常详细,需要的朋友们可以参考借鉴,下面来一起看看吧。...2020-06-25
  • C#中实现任意List的全组合算法代码

    这篇文章主要是介绍了.net C# 实现任意List的全组合算法实现代码,需要的朋友可以参考下...2020-06-25
  • C#常用数据结构和算法总结

    这篇文章主要介绍了C#常用数据结构和算法,这里我们总结了一些知识点,可以帮助大家理解这些概念。...2020-06-25
  • JS实现的随机排序功能算法示例

    这篇文章主要介绍了JS实现的随机排序功能算法,结合具体实例形式分析了javascript常用的排序算法实现技巧,需要的朋友可以参考下...2017-06-15
  • C++实现的O(n)复杂度内查找第K大数算法示例

    这篇文章主要介绍了C++实现的O(n)复杂度内查找第K大数算法,结合实例形式分析了算法的原理以及具体实现方法,需要的朋友可以参考下...2020-04-25
  • c# 实现位图算法(BitMap)

    这篇文章主要介绍了c# 如何实现位图算法(BitMap),文中讲解非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-11-03
  • 一篇文章带你搞懂Vue虚拟Dom与diff算法

    这篇文章主要给大家介绍了关于Vue虚拟Dom与diff算法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-08-26
  • R语言关于随机森林算法的知识点详解

    在本篇文章里小编给大家整理的是一篇关于R语言关于随机森林算法的知识点详解内容,有兴趣的朋友们可以跟着学习下。...2021-05-13
  • C++并查集亲戚(Relations)算法实例

    这篇文章主要介绍了C++并查集亲戚(Relations)算法,实例分析了并查集亲戚算法的原理与实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-04-25
  • C/C++实现八大排序算法汇总

    这篇文章主要为大家详细介绍了C/C++实现八大排序算法汇总,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • 基于稀疏图上的Johnson算法的详解

    本篇文章介绍了,稀疏图上的Johnson算法的详解。需要的朋友参考下...2020-04-25
  • C# URL短地址压缩算法及短网址原理解析

    这篇文章主要介绍了C# URL短地址压缩算法及短网址原理解析,本文重点给出了算法代码,需要的朋友可以参考下...2020-06-25
  • VC++实现选择排序算法简单示例

    这篇文章主要介绍了VC++实现选择排序算法简单示例,代码简洁易懂,有助于读者对数据结构与算法的学习,需要的朋友可以参考下...2020-04-25
  • php回溯算法计算组合总和的实例代码

    在本篇文章里小编给大家整理的是一篇关于php回溯算法计算组合总和的实例代码,有需要的朋友们可以学习参考下。...2021-07-14
  • JavaScript常用数组算法小结

    在开发项目的过程中,我们经常会需要关于javascript数组的一些算法,比方说数组去重、数组求交集、数组扰乱等等。今天就把个人的汇总整理的算法分享给大家。...2016-02-18
  • C++基于递归算法解决汉诺塔问题与树的遍历功能示例

    这篇文章主要介绍了C++基于递归算法解决汉诺塔问题与树的遍历功能,简单描述了递归算法的原理,并结合实例形式分析了基于递归算法解决汉诺塔问题与数的遍历相关操作技巧,需要的朋友可以参考下...2020-04-25
  • c++11新增的便利算法实例分析

    这篇文章主要介绍了c++11新增的便利算法,主要有用于判断、查找、数组、序列等的操作算法,非常具有实用价值,需要的朋友可以参考下...2020-04-25