集合元素全排列的生成(排列树+递归回溯)

1. 问题定义
  • 输入整数n,按照字典序从小到大输出前n个数得到所有排列。
    即若n=3,则有全排列 { 123 , 132 , 213 , 231 , 312 , 321 } \{123,132,213,231,312,321\} { 123132213231312321}
  • 或者输入含有n个元素的集合,输出这n个元素的 A n n A_n^n Ann 种排列结果。
2. 递归生成1~n的排列

根据我们上面举的关于字典序排列生成的方式,我们可以用递归的方式来生成排列:
即对于 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n 的排列,我们先生成1开头的排列,再生成2开头的排列,接着是3开头的排列,…,依此类推,最后是n开头的排列。

子问题的定义:

  • 生成1开头的排列的时候,我们第一位是1,剩下是 2 , 3 , . . , n 2,3,..,n 2,3,..,n 组成的子排列,所以我们递归的时候主要看首元素和子排列的关系。
  • 即我们递归函数需要保留以下参数:
    • 已经确定的“前缀序列A”,便于输出;
    • 还需要进行排列的元素集合S,便于从中选取下一元素。
  • 所以我们有伪代码
void permutation(前缀A,序列S){
   
	if(序列S为空)  输出序列A;
	else{
   
		按照从小到大的顺序依次取出S中每个元素u{
   
			permutation(A + u, S - {
   u});
		}
	}
}

实现一:暴力实现

基于上面的伪代码,我们可以先写一个简单的实现:

void permutation(int n, int* A, int index) {
   
	/*
	** A表示生成的排列序列,n表示排列的元素个数,index表示当前生成元素的位置
	*/
	if (index == n) {
    // 一个排列生成结束
		for (int i = 0; i < n; i++) printf("%d ", A[i]);
		printf("\n"); return;
	}
	// 按照从小到大依次取出1-n生成排列
	for (int i = 1; i <= n; i++) {
   
		int ok = 1; // 没有被取过
		for (int j = 0; j < index; j++) {
   
			if (i == A[j]) {
    ok = 0; break; }
		}// for
		if (ok) {
   
			A[index] = i;
			permutation(n, A, index + 1);
		}
	}
}

分析:
每次我们要从未选区的元素集合 S S S 中选择元素时,我们都需要先遍历 i 从 1 到 n i从1到n i1n 选择我们需要的元素,然后再遍历 j 从 0 到 i n d e x j从0到index j0index 判断该元素是否被选择过,导致当n比较大时,越是到后面的选取,我们越是要遍历两边数组,即复杂度接近 O ( n 2 ) O(n^2)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值