全排列递归的理解

把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日)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

取啥都被占用

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值