void Swap(int *a, int *b){
int tmp = *a;
*a = *b;
*b = tmp;
}
void Output(int A[], int size){
int i;
for(i = 0; i < size; i++){
printf("%d ", A[i]);
}
putchar('\n');
}
int cnt=0;//全局变量;
/* 可以解决待排列元素中没有重复元素的情况 */
void Full_Permutation(//相对外层调用/*随着递归的深入, p_size和end始终没有改变,方便递归 */
int A[], /* 待排列数所在的数组(始终是这个数组) */
int begin/* 起始元素数组索引(随着递归的深入和浅出的变化而变化) */,
int end/* 末尾元素数组索引(为不变量) */,
int p_size/* 初始待排列元素的数目(为不变量) */
){
printf("cnt=%d\n",++cnt);
int i;
/* 排列到最后一个元素时输出数组A
(打印该序列) */
if(begin >= p_size){//小心这不是递归出口,它只是一个在合适时机打印内容的打印语句;
printf("\n");
Output(A, p_size);
}
else{
/* 遍历各个位置 ;
i初始化为begin索引;*/
for(i = begin; i <= end; i++){//递归出口:i>end的时候开始"归的过程"
//for(int k = 0;k<i;k++) {printf("\t");};printf("before Swap(A + begin=&A[%d], A + i=&A[%d]) :",begin,i);Output(A, p_size);
/* 对于当前层次的for而言,begin的值是不会改变的,而i在当前层次的for是要逐渐的增长的. */
Swap(A + begin, A + i);/* 交换当前规模下的待排序列的首元素和第i个元素 */
//for(int k = 0;k<i;k++) {printf("\t");};printf("after Swap(A + begin=&A[%d], A + i=&A[%d]) :",begin,i);Output(A, p_size);printf("\n");
for(int k = 0;k<i;k++) {printf("\t");} printf("before %dth invoke, Full_Permutation(A, begin + 1= %d, end=%d, p_size=%d);",i+1,begin+1,end,p_size);Output(A, p_size);
/* 暂时先忽略细节:把它当成一个可以完成特定功能的黑箱,不要急着进入,否则越来越深,看不出连贯的逻辑; */ //invoke deeper:
Full_Permutation(A, begin + 1, end, p_size);/*相对内层调用(数组A相对的外层的那个数组A,排列起始位置改变);p_size和end也始终没有改变*/
for(int k = 0;k<i;k++) {printf("\t");} printf("after %dth invoke, Full_Permutation(A, begin + 1= %d, end=%d, p_size=%d);",i+1,begin+1,end,p_size);Output(A, p_size);
/* 打印完子级规模调用所计算出来的排列结果后,将本调用之初所交换的那两个元素换回去,以便下一个还未充当队头的元素能够顺利当上的对头同时不会让已经当过队头的元素再次当队头,避免了混乱;即事实上充当队头的元素是可以事先有序地安排好的.(即A[0]元素先当队头,计算并打印出所有A[0]打头的排列结果后,借助循环变量i,将A[1]元素A[0]元素交换(也就是和begin所指的元素进行交换,begin,begin在同一级别的调用中所指的位置(索引)不发生改变(当然位置里的值一般会变),在更深层的调用中则会随着深度的增加依次的增加1(往后面的位置指);),再次(递归)调用排列函数,以此来计算并打印以A[1]元素打头的所有排列结果,) */
//for(int k = 0;k<i;k++) {printf("\t");};printf("before Swap(A + begin=&A[%d], A + i=&A[%d]) process:",begin,i);Output(A, p_size);
Swap(A + begin, A + i);
for(int k = 0;k<i;k++) {printf("\t");};printf("after Swap(A + begin=&A[%d], A + i=&A[%d]) process:",begin,i);Output(A, p_size);printf("\n");
}
}
}
/* 解决含有重复元素的带排列数: */
void Full_Permutation_Duplicate(int A[], int begin, int end, int p_size)
{
int i, j;
if (begin >= p_size)
{
Output(A, p_size);
}
else
{
for (i = begin; i <= end; i++)//这个循环目的是让(不同/不重复的)元素都有机会当排列结果的打头元素.(反正都要拿来排列,可以从集合的角度排看待重复问题)
{
/* 类似选择排序,每一轮定下一个固定的参照元素A[i], */
for (j = begin; j < i; j++)
{
if (A[j] == A[i])
{
break;//此时必有 (i!=j,将导致A[i]不打头(因为它之间某个元素重复了,就不让这个元素(值)再打头),保证了(每一规模级别都不会出现重复打头的值))
}
}//如果不是由于break而离开for的话,那么有i==j
if (i == j)
{
Swap(A + begin, A + i);
Full_Permutation_Duplicate(A, begin + 1, end, p_size);
Swap(A + begin, A + i);
}
}
}
}
int main() {
int B[5] = {1, 1, 2, 2, 3};
Full_Permutation_Duplicate(B, 0, 4, 5);//duplicate重复/复制
int A[3] = {1, 2, 3};
int C[4] = {1,2,3,4};
int D[5] = {1,2,3,4,5};
Full_Permutation(A, 0, 2, 3);
//Full_Permutation(C,0,3,4);
//Full_Permutation(D,0,4,5);
}