全排列大家都知道。这里是通过最普遍的全排列,来理解回溯法的一个实现。最普通的全排列,比如A B C 三个字母,每个字母只能用一次,来进行排列,共有6种结果,比如ABC,ACD等。这个全排列结果和我们现实生活中N个人排队的可能性的结果是一样的。
我这边的思路就是按照位置来进行,比如N个元素,首先第1个位置(索引为0)可以有N种选择,那么第二个位置有N-1种选择,倒数第二个位置有2种选择,最后一个位置只能有1种选择。
当我们完成一次排列之后,回溯,首先回溯到倒数第二个位置,倒数第二个位置有2种选择,第一次排列已经用了其中一个选择,回溯之后,我们采用另外一个选择,最后一个位置也有一个选择,这样又完成了一次排列。
完成第2个排列之后,我们再次回溯到倒数第二个位置,发现2种选择都试过了,那么回溯到倒数第3个位置,倒数第3个位置有3种选择,各个选择我们都用一遍之后,继续回溯到倒数第4个,如此回溯直到到第1个位置(索引为0,有N个选择)。N个选择都用一遍,直到把所有的排列情况都尝试了一遍。
明显可以看出,整个回溯过程,其实就是图的深度优先搜索DFS。
OK,下面是实现的代码:
#define STR_SIZE 6
char g_data[STR_SIZE] = {'A','B','C','D','E','F'};
char g_output[STR_SIZE] = {0};
unsigned int g_fullPermutationCount = 0;
void FullPermutationTest()
{
FullPermutation(0);
printf("\r\nTotal count of full permutation = %d\r\n",g_fullPermutationCount);
}
void PrintFullPermutation()
{
printf("One of result:\r\n");
for(int i=0;i<STR_SIZE;i++)
{
printf("%2c",g_output[i]);
}
printf("\r\n");
}
//判断index位置选择的元素在前面index-1个位置中是否有选择过
BOOL IsSelect(int index)
{
for(int i=0;i<index;i++)
{
if(g_output[i] == g_output[index])
{
return TRUE;
}
}
return FALSE;
}
void FullPermutation(int index)
{
for(int i=0;i<STR_SIZE;i++)
{
g_output[index] = g_data[i];
if(!IsSelect(index)) //表示前面index-1个位置都没有选择过该元素(g_data[i])
{
if(index == STR_SIZE-1) //全部位置都选择了,那么完成了一个排列
{
PrintFullPermutation();//输出排列
g_fullPermutationCount++;
}
else
{
FullPermutation(index+1);//搜索下一个位置的排列
}
}
//继续执行下一个循环,也就是回溯,尝试其他的可能性
}
}
测试结果如下:
当然,实际情况的排列可能会有出现重复的字母(比如2个A,或者3个B),那么排列就不一样了。但是还是可以通过如上的回溯稍加变化来实现。