递归函数:以层次来想函数递归,以深度来想递归出口。
问题:
给出一个集合,输入这个集合所有的排列集合。
例如:
输入:
{1,2,3}
输出:
{1,2,3}
{1,3,2}
{2,1,3}
{2,3,1}
{3,1,2}
{3,2,1}
思路:
全排列,就是不断交换两个元素,打印。
将所有可以交换的两个元素都交换一遍,都打印一遍,加上本来的排列,打印出来的就是我们的全排列。
所以我们焦点就放在了交换上面,怎么交换两个元素会使逻辑清晰。我们自己手写全排列的时候,一般都是先以第一个元素为首的全排列写完,再以第二个元素为首的全排列写完…直至写完。
我们先确定1这个位置不动,对剩下的数进行全排列,剩下的数的全排列第一个数加上1,那么以1为首的全排列就结束了。那么怎么对剩下的全排列进行排序呢?剩下的元素中,第一个元素不动,对剩下的元素进行全排列…一直递归,那么递归出口就是我们递归到最后一个元素,不需要对最后一个元素进行全排列。
看着代码分析会清晰一点:
void Perm(int* arr, int k, int m)
{
if (k == m - 1)
{
for (int i = 0; i < m; i++)
{
printf("%2d", arr[k]);
}
}
else
{
for (int i = k; i < m; i++)
{
swap(arr[i], arr[k]);
Perm(arr, k + 1, m);
swap(arr[i], arr[k]);
}
}
}
我们以层次来分析这个代码,首先将第一个元素和第一个元素本身进行交换,再将第一个元素本身和第二个元素交换,第一个元素和第三个元素交换…直至第一个元素和最后一个元素进行完交换。
第一层:产生【1,2,3】 【2,1,3】 【3,2,1】三个数组。下一层递归,会对数组的除去第一个元素的剩下元素进行交换。剩下元素的第一个元素和第一个元素交换,剩下元素的第一个元素和第二个元素交换,剩下元素的第一个元素和第三个元素交换…
递归函数我们按层次分析,出来的就会是一个树,在这里我们相当于在叶子节点时,打印数组的值。