把array[] = {1,2,3} 全排列。这个递归式到处都能见到。但是理解起来,我个人觉得很绕,因为不能只简单想A33。由于目前对于gdb还是不熟。以下附上print的方式帮助自己理解。
#include<stdio.h>
void perm(int *data, int n, int curr) {
int i,m;
if (curr == n-1){
for (m=0; m<n;++m)
printf("%d", data[m]);
printf("\n");
}
else {
for (i=curr; i<n; ++i) {
int t;
t=data[curr],data[curr]=data[i],data[i]=t;
printf("line1: curr = %d, i = %d\n", curr,i); //watch point 1
perm(data,n,curr+1);
printf("line2--after perm(curr+1): curr = %d, i = %d\n", curr, i); //watch point 2
t=data[curr],data[curr]=data[i],data[i]=t;
}
}
}
int main() {
int z;
int array[] = {1,2,3};
perm(array,sizeof(array)/sizeof(array[0]),0);
for (z=0;z<3;z++) {
printf("%d",array[z]);
}
printf("\n");
return 0;
}
以上的两个watch point,可以很好的看出程序运行的路线。运行结果如下,
接下来我用自己的描述方式走一遍从print 123到print 123,帮助以后的我回头看这篇文章带来一些直观的提醒。
首先程序从 perm({1,2,3}, 3,0)出发,一开始curr=i=0。t=data[0], data[curr]=data[i]=1, data[0]=t。这里的data[0] = 1。
接着,程序遇到了perm({1,2,3},3,0+1),因为curr = 1, 1<3-1,所以进入line 1 部分,这个时候,curr = i = 1。t=data[1], data[curr] = data[i]=2, data[1]=t。这里data[1]=2。
接着,程序遇到了perm({1,2,3},3,1+1),因为curr = 2,进入if部分,print 123。
*
程序出来后进入line 2部分,curr = 1,i = 1。data[]间没有换位。
因为i<3,i增成了2,进入line 1部分。curr = 1, i = 2。data[1]与data[2]换位了。此时数组变成了 {1,3,2}。程序去到了({1,3,2}, 3, 1+1),于是又print出了 1,3,2。
*
出来后程序来到了line 2,此时 curr = 1, i = 2。所以 data[1]与data[2]换位,此时数组成了{1,2,3}。i于是又增成了3。for 的条件此时不满足了。我把这叫奔溃。
程序“奔溃后”它要开始倒退了。此时会发现最初的那个perm({1,2,3}, 3,0+1)后,咱还没往下走过呢。程序这时候就是退到了那里。再次进入line 2部分。
回到了最上面那个红字的状态curr=0, i=0。line2下面那个转换无变化。这时候因为i=0,我们继续走循环,i被加了1,即 i = 1。而curr 仍为0 (i = 1, curr = 0)。数组{1,2,3}中的1与2换位。
数组成为了{2,1,3},然后接着走去perm({2,1,3},3,0+1),数组又来到 line 1部分,curr=1, i=1。没有换位发生数组再去perm({2,1,3},3,1+1),print 出了213。
*
完事后,程序去到了line2部分。到了上面的第二处红字 curr=1, i=1。line2部分没有发生转换。i又增加了1,成为 i = 2。这时候程序去到了line1部分并发生了转换,是data[1]与data[2]间的转换,成为数组{2,3,1}。程序走到了perm({2,3,1},3,1+1),打印出了231。
*
接着程序又来到了line 2 部分。curr = 1, i = 2。先换位成为数组{2,1,3}。接下来程序再奔溃。
程序开始往后退,退到 curr = 0, i = 1那会儿 (记得最初那个curr = 0, i = 0么,)。然后数组换位成{1,2,3}。接下来去line1部分,i 增1。此时状态curr = 0, i = 2。数组成了{3,2,1}。然后又去到了perm({3,2,1},3,0+1}。到了line1部分。curr=i=1。所以没动静。又去到perm({3,2,1},3,1+1}。数组{3,2,1}被打印出了。
*
此时,curr=1, i = 1。去到line2 那里,所以没动静。再循环去line1, i = 2而curr=1。于是数组换位成{3,1,2}。去到了perm({3,1,2},3,1+1)。然后打印出来了 312。
*
此时,以下都发生在line2部分: 【curr=1, i = 2。数组换位成{3,2,1}。i 是2了,所以程序又奔溃啦。回到了第二个最初,既 curr = 0, i = 0与curr = 0, i = 1之后。这里就是curr = 0, curr = 2, 然后0,2换位,数组成了{1,2,3}】。
程序再奔溃。curr = 0, i = 2之后,程序不满足for语句了。也自然不会去上面的那个perm(...curr+1)的部分。 它跳出了整个的perm function。来到了main。print了123。
***
图片中的三种颜色的框就是三次最大的"退回"。颜色一一对应。递归走人脑,真是好考记忆力啊。
***
哭了...终于写完了。为啥我脑海里蹦出了电影蝴蝶效应,别说那电影是说蝴蝶效应的。这男主人公奔溃后老是回过去改历史,虽然改历史的内容是任意他说了算的,不像递归。但是奔溃后老是回到前点,还真是递归味很浓啊。电影应该改名叫 Recursive Butterfly Effect
***
如果真的有人看我这篇文章,如果你有更好的方式理解递归,希望不吝赐教!低手在这里感激不尽!
后记:今天随意看回这篇文章,发现自己之前写的东西真是挺搞笑的,哈哈。由衷觉得学习的历程很奇妙。虽然写的内容通篇连入栈出栈这样的关键字都看不到,但是道理还是一样的,而且这样写也许反而适合用来底层学习用。(2015年02月23日)