C语言学习之 没有重复项数字的全排列

题目描述

给出一组数字,返回该组数字的所有排列

例如:

[1,2,3]的所有排列如下
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1].
(以数字在数组中的位置靠前为优先级,按字典序排列输出。)

数据范围:数字个数 0<n≤6

要求:空间复杂度 O(n!) ,时间复杂度 O(n!)

题目分析

        题目中是给定一个数组,该数组是没有重复项的,纯数字的。要求输出的是其内部元素排列组合后形成的多个数组。

        根据题意可知,需输出的是一个二维数组,行数为数组元素个数的排列组合数,我们需要一个内存空间存放这么多数组。题目中源数组长度最大为6个,即最多有6*5*4*3*2*1 =720个数组结果。考虑到实际问题可能需要更多,我们采用申请动态空间的方式开辟这些数组所需的内存。

        根据题意,输出的是一个个的数组,所以需要每个数组对应的行数、列数。

        根据题意,输出结果要“以数字在数组中的位置靠前为优先级,按字典序排列输出”。

        这一题对我来说相对困难,经过多次尝试后,最终选择使用递归的思路求解。

        递归思路中,需要将问题逐步分解,使所有操作都能由一个简单程序完成。题目中排列组合,在之前的课中有类似练习,当时是通过交换两个元素的思路完成的,这里想到递归之后,第一时间我就想到用交换的思路。

        以题目中 1 2 3 数组为例,将1 分别与2、3交换,就能得到1 2 3 ;2 1 3;3 1 2。再对1 2 3 ; 2 1 3 ; 3 1 2中的后两位分别进行交换,就能得到1 3 2 ;2 3 1;3 2 1。同理,其他长度的数组也可以使用这种思路排列。

        题目中又要求顺序,所以需要考虑交换后的数组顺序是否为题目要求的,如不满足,还需进行调整。

        基于以上思路,进行如下编码。

代码如下:

#include <stdio.h>	//NULL标识需要引用 stdio.h 文件
#include <malloc.h>	//申请动态内存需要引用 malloc.h 文件

void swap(int* a, int* b)	//将地址a和地址b中的数值交换
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//设置一个索引 index,用于调换源数组不同位的元素,以及返回调用前位置
void re_permute(int* num, int index, int numLen, int* returnSize, int** returnColumnSizes, int** res)
{
	int i = 0;
	if (index == numLen)	//索引到最后一位时,将num数组赋值给当前行数组
	{
		res[*returnSize] = (int*)malloc(numLen * sizeof(int));	//申请动态内存,存放一行数组
		for (i = 0; i < numLen; i++)	//数组循环赋值
		{
			res[*returnSize][i] = num[i];
		}
		(*returnColumnSizes)[*returnSize] = numLen;	
		//对应行数组列数,即数组长度,为numLen
		(*returnSize)++;	//每次赋值后,行数+1
	}

	for (i = index; i < numLen; i++)	//从索引
	{
		swap(&num[i], &num[index]);		//每一位轮流与索引位交换
		//数组较长时,需两次交换满足题目要求,执行以下判断
		if ((numLen - index) > 2 && index != i)	
		{
			swap(&num[i], &num[index + 1]);	//保证交换顺序符合题目要求
			re_permute(num, index + 1, numLen, returnSize, returnColumnSizes, res);
			//索引位+1,到末位时,整个数组排序结束,再+1,进入数组赋值环节
			swap(&num[i], &num[index + 1]);	//后交换的先复原
		}
		else
		{
			re_permute(num, index + 1, numLen, returnSize, returnColumnSizes, res);
			//索引位+1,到末位时,整个数组排序结束,再+1,进入数组赋值环节
		}
		swap(&num[i], &num[index]);	//每一位轮流与索引位交换,即还原
	} 

}

// @param num int整型一维数组
// @param numLen int num数组长度
// @return int整型二维数组
// @return int* returnSize 返回数组行数
// @return int** returnColumnSizes 返回数组列数

int** permute(int* num, int numLen, int* returnSize, int** returnColumnSizes) 
{
	// write code here
	if (numLen == 0)
		return NULL;
	int i = 0;
	int count = 1;
	for(i = 1; i <= numLen; i++)	//计算所需的行数
		count *= i;
	*returnColumnSizes = (int*)malloc(count * sizeof(int));	
	//每一行对应一个列数,将列数以数组形式记录,方便后续调用
	//申请动态内存,存储列数数组
	int** res = (int**)malloc(count * sizeof(int*));	
	//申请动态内存,存放结果数组
	*returnSize = 0;			//数组列数,从0开始计算						
	re_permute(num,0,numLen, returnSize,returnColumnSizes, res);	
	//将res数组地址作为参数,方便改动其中内容
	
	return res;	//前面是地址传参,这里可以直接返回数组首元素地址。
}

        代码中,将交换函数、排列函数分别作为子函数,方便调试和理解。在主函数中,先判断数组中有无数字;再为数组行数、列数申请动态内存。

        交换函数没有什么特别的,就是地址指向的元素的交换。

        排列函数中,先对递归的尽头进行定义,防止出现死递归。递归的尽头就是数组所有元素都排列完,这时就可以将该排列输出。然后是递归的主体,本着按顺序排列组合的思路,依次对数组首位(index = 0),第二位(index=1),第三位,第四位...进行交换。考虑题目要求顺序,又对交换后顺序进行调整,最后进行输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值