假设以二维数组g[1..m][1..n]表示一个图像区域,g[i][j]表示该区域中点(i,j)所具颜色,其值为从0到k的整数。试编写递归算法,将点(i0,j0)所在区域的颜色置换为颜色c。约定与(i0,j0)同色的上、下、左、右的邻接点为同色区域的点。
表示图像区域的类型定义如下:
/* 在g[1..m][1..n]中,将元素g[i0][j0] */
/* 所在的同色区域的颜色置换为颜色c */
typedef char GTYPE[m+1][n+1];
1 voidChangeColor(GTYPE g,int m,int n,char c,int i0,int j0) 2 { 3 char temp; 4 5 //判断是否在合法区域内 6 if(i0 <1|| i0 > m || j0 <1|| j0 > n) 7 return; 8 9 //防止进入死递归 10 temp = g[i0][j0]; 11 if(c == temp) 12 return; 13 14 g[i0][j0]= c; 15 //依次判断上、下、左、右点 16 if(i0 -1>=1&& g[i0-1][j0]== temp) 17 ChangeColor(g,m,n,c,i0-1,j0); 18 if(i0 +1<= m && g[i0+1][j0]== temp) 19 ChangeColor(g,m,n,c,i0+1,j0); 20 if(j0 -1>=1&& g[i0][j0-1]== temp) 21 ChangeColor(g,m,n,c,i0,j0-1); 22 if(j0+1<= n && g[i0][j0+1]== temp) 23 ChangeColor(g,m,n,c,i0,j0+1); 24 }
思路:总体思路是先替换再查找。从指定的点开始,先保存该点的颜色值temp再替换成c颜色值;然后再分别判断区域内上下左右的颜色值是否等于temp,如果等于,则利用相同的方法,递归地判断下一个点;直到所有符合要求的点被替换成c颜色值为止。
有些读者会提出:可不可以把替换颜色值的操作放在其中一个if语句之后,也就是让函数先查找下一个点,再替换指定点的颜色值?
我们假设将替换颜色值的语句放在最后一个if之后(其他情况类似),代码如下
1 voidChangeColor(GTYPE g,int m,int n,char c,int i0,int j0) 2 { 3 char temp; 4 5 //判断是否在合法区域内 6 if(i0 <1|| i0 > m || j0 <1|| j0 > n) 7 return; 8 9 //防止进入死递归 10 temp = g[i0][j0]; 11 if(c == temp) 12 return; 13 14 //依次判断上、下、左、右点 15 if(i0 -1>=1&& g[i0-1][j0]== temp) 16 ChangeColor(g,m,n,c,i0-1,j0); 17 if(i0 +1<= m && g[i0+1][j0]== temp) 18 ChangeColor(g,m,n,c,i0+1,j0); 19 if(j0 -1>=1&& g[i0][j0-1]== temp) 20 ChangeColor(g,m,n,c,i0,j0-1); 21 if(j0+1<= n && g[i0][j0+1]== temp) 22 ChangeColor(g,m,n,c,i0,j0+1); 23 24 g[i0][j0]= c; 25 }
我们知道函数每调用一次都会将函数实参、函数里的局部变量都压栈保存,所以当你调用ChangeColor函数时都会在栈中保存g(二维函数指针,占4个字节)、数组范围m和n、替换的颜色值c、指定点坐标i0和j0以及函数里的局部变量temp;而且每个函数栈在一般情况下都是独立、互不影响的,这样才能保证函数的正常运行。
当我们调用ChangeColor(g,6,4,2,4,3)想要将(4,3)点同色区域替换成颜色值2时,此时会将函数的实参和局部变量temp压栈,我们简单用(4,3)表示。根据函数代码,会先查找指定点的上面的点(3,3),因为(3,3)颜色值为1,将(3,3)压栈,继续在(3,3)指定点的基础上查找上面的点,此时该点颜色值为0不符合;接下来指定点(3,3)会继续查找下面的点,此时由于(4,3)的颜色值为1,还没被替换为颜色值2,符合替换要求,将(4,3)压栈,那么问题来了,兜了一圈又回到了原来的点,这样便会无限地递归下去。因为栈的空间是有限的,每次函数调用都会压栈,从而出现栈溢出。
当一个点跳到另一个点之前没被替换,而跳回来时也没改变便会进入死递归中,直到栈溢出发生错误,所以需要在所有跳转之前,执行颜色值替换操作。
细心的读者可能会问:如果替换的颜色值c和开始指定点的颜色一样,那会发生什么?
毫无疑问是死递归最后栈溢出。上面已经说过为了防止“一个点跳到另一个点之前没被替换,而跳回来时也没改变”这种情况,需要在所有跳转之前,执行颜色值替换操作。但是此时替换的颜色值和指定点的颜色值一样,那根本和没替换没什么区别,当一个点跳到另一个点后,之后也一定会跳回来,从而进入特殊的死递归。
最后通过以上一个简单的递归例子总结一句话:语言再高级,算法不严谨也没用!
延伸阅读:http://www.nowamagic.net/librarys/veda/detail/2314