排列与组合问题

这里利用了回溯思想:全排列相对简单,不过以下代码待优化,可以如果把数组换成链表,每挑出一个数,就把这个数删除,下一层就可以少遍历一次,不用使用visited[]数组来判断是否挑过它了,浪费时间,还浪费空间。

void GetPermutation(int set[],int result[],int length,int deepth = 0)
{
	if(deepth == length)
	{
		cout << "{";
		for(int i = 0; i < length; i++)
		{
			cout << result[i] << ",";
		}
		cout << "}" << endl;
	}
	else
	{
		for(int i = 0; i < length; i++)
		{
			if(!visited[i])
			{
				visited[i] = true;
				result[deepth] = set[i];
				GetPermutation(set, result, length, deepth + 1);
				visited[i] = false;
			}
		}
	}
}

组合问题相对复杂一点,假如从1,2,3,4,5中抽出3个数,如果不加思考,上来就用回溯,则会出现挑选重复的问题,比如第一次我挑了 1,2,3。后来我还可能挑出来2,1,3.这个是不允许的,怎样去掉重复呢,顺着这个思路走就入坑了,因为先有重复再去重复这思想就不好,去重复是很复杂的,难不成要把所有出现的结果专门记录下来排下序,每次新产生一个结果就排序跟以前所有的比较吗?不可想象,还是多想想策略吧,比如:我第一轮是以1开头,之后的数我只看1后面的数,第二轮以2开头,之后的数就只挑2后面的数,1就不在挑选范围了,因为上一轮1开头的时候已经把包含1所涉及的所有情况都挑出来了。这样就好理解了,

上代码:仍然是递归式回溯

int n = 3;
void combination(int data[],int length, int result[] ,int deepth = 0, int index = 0)	//data为原数组,length为数组长度,result为临时数组存储这次保存的结果
{
	if(deepth == n)	//deepth为n意味着,组合数的个数已经凑齐
	{
		cout << "{";
		for(int i = 0; i < n; i++)	//循环输出凑齐的n个数
		{
			cout << result[i] << ',';
		}
		cout << "}" << endl;
	}
	else  //如果没凑齐
	{
		int tempLength = length;	//临时变量近在第一次调用函数的时候用,第一次循环到length-2之前就停止,因为后它后面个数不够n个了,肯定凑不齐
		if(index == 0)
		{
			tempLength = length - 2;	//仅在第一次函数运行时确定循环到length-2
		}
		for (int i = index; i < tempLength; i++)
		{
			result[deepth] = data[i];	//存储数据至临时数组
			combination(data, length, result, deepth + 1, i + 1); //进入下一层
		}
	}
}
组合问题其实可以用循环实现,但是循环的层数在写出来的时候就是固定的,要挑几个数就得写几层循环,对于挑选n个数,就得写n层循环,不能随意变通,这里递归的形式比较灵活一些。


猜你喜欢

转载自blog.csdn.net/m0_37907835/article/details/78984587