Logo
Section 1 / 4

排列与组合

学习数据结构中排列与组合的基础知识。

导入

排列与组合

学习数据结构中排列与组合的基础知识。

组合数学简介

数学中以计数为主要研究内容的领域被称为组合数学。它在计算机科学中有广泛的应用。

组合数学的计数能力及其他抽象概念被应用于各种算法中以获得结果。它还研究有限结构的某些性质,例如计算机科学中的数据结构。本质上,所有数据结构都与某个有限集合的某种抽象相关联。我们可以对有限集合应用不同类型的排列,这种研究在组合数学中也占据重要地位。

...
排列关注顺序,而组合不关注

当数据结构需要不同类型排列时,也会相应地产生不同类型的算法。

对于同一个问题,如果需要进行枚举或计数,可能存在多种不同的算法。我们可以根据自己的方式来解决这些问题。

此外,为了更深入理解组合数学与数据结构及算法之间的关系,我们需要了解不同算法作用于数据结构的基本操作。

我们已经知道,特定数据结构的枚举是组合数学的一部分。某种数据结构的排列同样属于组合数学范畴。我们可以重新组织或排列任意字符串的字符,形成不同的组合。

此外,我们还可以检查一个数据结构是否满足某些条件。如果有多种可能的情况,也可以使用算法来判断最优的数据结构或解决方案是什么。

我们现在知道,组合数学涉及对有限集合或数据结构的不同类型排列;因此,我们需要进一步学习排列与组合,以掌握组合数学的核心抽象概念。

当允许重复时,组合的数量会增加;当不允许重复时,组合的数量或重排的数量则会减少。

当数据结构需要不同类型的排列时,也会产生不同类型的算法。
对于同一个问题,如果需要枚举或计数,可能存在多种不同的算法。我们可以根据自己的方式来解决这些问题。
为了更深入理解组合数学与数据结构及算法的关系,我们需要了解不同算法作用于数据结构的基本操作。
我们已经知道,特定数据结构的枚举是组合数学的一部分。某种数据结构的排列也属于组合数学范畴。我们可以重新组织或排列任意字符串的字符,形成不同的组合。
此外,我们还可以检查一个数据结构是否满足某些条件。如果有多种可能的情况,也可以使用算法来判断最优的数据结构或解决方案是什么。
我们现在知道,组合数学涉及对有限集合或数据结构的不同类型排列;因此,我们需要进一步学习排列与组合,以掌握组合数学的核心抽象概念。
当允许重复时,组合的数量会增加;当不允许重复时,组合的数量或重排的数量则会减少。

组合与排列的区别

让我们看一个例子。以单词 "forget" 为例。如果我们查词典,会发现许多意思相近的词语。

它们包括 "overlook"(忽略)、"drop"(落下)、"neglect"(忽视)、"omit"(省略)、"leave out"(遗漏)等。我们可以将这些词语以多种方式重新排列,且不关心顺序。当我们不关心词语顺序时,这被称为组合。

然而,当我们按字母顺序重新排列这些词语时,结果如下:drop、leave out、neglect、omit、overlook 等。当顺序在组合中起作用时,这被称为排列。

想想一个组合锁。我们可以从 0 到 9 中选择任意三个数字。如果组合为 562 时能打开,说明这个排列中不允许重复。如果是 223,则说明允许重复。

在任何不允许重复的排列中,可能性的数量就是所要重排元素数量的阶乘。

考虑任意三个元素。有多少种方式可以重新排列它们?我们可以这样计算:

3!=3×2×1=63! = 3 \times 2 \times 1 = 6

有六种方式可以重新排列三个元素。如果有四个元素,则有 24 种重排方式,因为 24 是 4 的阶乘。

不允许重复的排列的 Java 代码

理论讲得够多了,现在进入第一个代码示例,我们将找到计算无重复排列数量的最佳算法。

1//SimplePermutation 2class Main{ 3 static int permutationWithoutRepetition(int n){ 4 int i = 1; 5 int k = 1; 6 while (i <= n){ 7 k *= i; 8 i++; 9 } 10 // 我们得到了传入参数的阶乘 11 return k; 12 } 13 public static void main(String[] args) { 14 // 无重复排列的公式是:factorial(listOfNumbers)/factorial(listOfNumbers - restOfNumbers) 15 16 int listOfNumbers = 5; 17 int restOfNumbers = 3; 18 System.out.println("数字列表为 (5, 4, 3, 2, 1)"); 19 System.out.println("我们的第一个选择有五种可能。"); 20 System.out.println("这五个数字可能的排列数量为:"); 21 System.out.println(permutationWithoutRepetition(5)); 22 } 23}

输出显示,当有 5 个元素需要重排时,共有 120 种可能,即 5 的阶乘。

1数字列表为 (5, 4, 3, 2, 1) 2我们的第一个选择有五种可能。 3这五个数字可能的排列数量为: 4120

在上述代码中:

  • 第 3–12 行:静态函数 permutationWithoutRepetition() 使用以下公式计算可能的排列数量:

Pkn=n!(n−k)!{{P}^n_k} = \frac {n!}{(n-k)!}

这只是一个小例子。实际上,成百上千种组合算法基于有限集合处理不同类型的数据结构,甚至包括由图构建的结构。

稍后我们会讨论这一点,但在那之前,先来看一些常见的组合算法示例。比如,我们想要生成某一类型的所有结构列表。在这种情况下,我们可以考虑带重复或不带重复的排列,也可以考虑带重复或不带重复的组合。

搜索算法是解决此类问题的优化和近似算法的良好范例。优化方法或算法还包括动态规划。另一方面,近似方法包括贪心算法。在解决问题之前,我们无法断定哪种算法最好。

所以,如果有五个元素,就有 120 种可能。现在我们从中选出两个,并尝试按顺序重新排列它们。但不能重复使用同一个元素。这是一种无重复的排列,虽然它与我们之前看到的情况有所不同。

如果我们想从五个元素中选出两个并进行无重复排列,其顺序有多少种?

1//SimplePermutation 2class Main{ 3 static int permutationWithoutRepetition(int n){ 4 int i = 1; 5 int k = 1; 6 while (i <= n){ 7 k *= i; 8 i++; 9 } 10 // 我们得到了传入参数的阶乘 11 return k; 12 } 13 public static void main(String[] args) { 14 // 无重复排列的公式是:factorial(listOfNumbers)/factorial(listOfNumbers - restOfNumbers) 15 16 int listOfNumbers = 5; 17 int restOfNumbers = 3; 18 System.out.println("数字列表为 (5, 4, 3, 2, 1)"); 19 System.out.println("我们的第一个选择有五种可能。"); 20 System.out.println("这五个数字可能的排列数量为:"); 21 System.out.println(permutationWithoutRepetition(5)); 22 System.out.println("现在我们需要从这个集合中任选两个数字。"); 23 System.out.println("从五个数字中任选两个的排列数量为:"); 24 System.out.println(permutationWithoutRepetition(5)/permutationWithoutRepetition(3)); 25 } 26}

运行上述代码后,得到以下输出:

1数字列表为 (5, 4, 3, 2, 1) 2我们的第一个选择有五种可能。 3这五个数字可能的排列数量为: 4120 5现在我们需要从这个集合中任选两个数字。 6从五个数字中任选两个的排列数量为: 720

在上述代码中:

  • 第 25 行:当我们从五个元素中选取两个时,剩下三个元素。算法非常简单:

    1. 计算 5 的阶乘。
    2. 计算 3 的阶乘。
    3. 将 5 的阶乘除以 3 的阶乘。

    这里我们直接使用上面的函数计算 P25=5!3!=20P_2^5=\frac{5!}{3!}=20。

正如我们之前所说,存在成百上千种组合算法。除了常见的排序和搜索算法外,还有用于生成排列与组合、构造图等的多种类型算法。

命题逻辑

排列:有重复与无重复