什么是排列
排列(permutation),数学的重要概念之一。有限集的子集按某种条件的序化法排成列、排成一圈、不许重复或许重复等。从n个不同元素中每次取出m(1≤m≤n)个不同元素,排成一列,称为从n个元素中取出m个元素的无重复排列或直线排列,简称排列。
从n个不同元素中取出m个不同元素的所有不同排列的个数称为排列种数或称排列数。用符号 A(n,m) 表示。
应用场景
排列比组合有更广阔的应用场景,因为与组合相比,排列还考虑了顺序问题。搜索所有排列同样也是暴力解决多种算法问题的思路,比如典型的各种旅行商问题。
算法思路
从length个元素中排列num个元素,可以创建一个长度为num的数组sequence,数组内存放的是0到length-1之间的不重复数字。
每一次生成排列,数组sequence内的规则如下:
- sequence数组从左到右依次确定各个元素
即先确定sequence[0],在确定sequence[1],最后确定sequence[num-1] - 每一次生成的排列都要确保不与生成过的重复
例如sequence曾经为{0,1,2,3,4},那么{0,1,2,3,4}将不再出现 - 每个元素都尽量的选取小的数
当length为8,num为5。sequence前四个元素分别为0,2,3,4。且{0,2,3,4,1}出现过。那么sequence[4]可以选取的数字为5,6,7,选择最小的5。 - 当sequence内的某个元素已经没有可选数字,则它的上一个元素重新选择。
当length为8,num为5,sequence 为 {0,2,3,4,7},此时sequence[4]已经没有选择空间,则sequence[3]重新进行选择。
由此可见,在这种规则下,可以推断:
- 当sequence[0]再也没有选择空间时,所有的排列已经搜索完毕。
源码分享
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int test(const int *p, int num);
int *sequenceAndTest(int length, int num, int (*testFunc)(const int *, int));
int main(int argc, char const *argv[])
{
sequenceAndTest(4, 2, test);
system("pause");
return 0;
}
/**
* @brief 测试函数,测试找到的排列
* @param p 指向组合的下标
* @param num 下标的数量
* @return
* 返回 0:不符合要求,继续搜索
* 返回 1:符合要求,停止搜索
*/
int test(const int *p, int num)
{
for (int i = 0; i < num; i++)
{
printf("%d ", p[i]);
}
printf("\r\n");
return 0;
}
/**
* @brief 搜索排列状态,并测试
* @param length 排列的长度
* @param num 选择排列的个数
* @param testFunc 测试函数的指针
* @return 返回指针,指向符合要求的结果,全部不符合则返回NULL
*/
int *sequenceAndTest(int length, int num, int (*testFunc)(const int *, int))
{
if (length <= 0 || length < num)
{
return NULL;
}
//初始化
int *sequence = malloc(num * sizeof(int));
int appoint = 0; //当前需要确定的数的下标
int minOpt[num]; //记录sequence[]中每一位可选择的最小值
memset(minOpt, 0, sizeof(minOpt));
while (1)
{
//sequenxe[appoint]选择
int i = 0;
for (i = 0; i < length; i++)
{
//先排除已经被前面选的情况
int j = 0;
for (j = 0; j < appoint; j++)
{
if (sequence[j] == i)
{
break;
}
}
if (j < appoint)
{
//i已经被之前选中,考察下一个i
continue;
}
if (i >= minOpt[appoint])
{
//该数字i可选
sequence[appoint] = i; //确定sequence[appoint]的选择
minOpt[appoint] = i + 1; //下次只能从更大的选
appoint++;
break;
}
}
if (i == length)
{
//sequence[appoint]没有选到数字
if (appoint == 0)
{
//第0个数字都没能选到,再也没有新的排列了
break;
}
minOpt[appoint] = 0; //sequence[appoint]可以重新选
appoint--;//上一个数字sequence[appoint-1]重新选择
}
if (appoint == num)
{
//新排列寻找完毕
if ((*testFunc)(sequence, num))
{
//测试通过
return sequence;
}
appoint--;//最后一位数字重新选择
}
}
//没有排列满足要求
free(sequence);
return NULL;
}
输出结果:
0 1
0 2
0 3
1 0
1 2
1 3
2 1
2 3
3 0
3 1
3 2