在递归函数内部打印关键值,配合缩进,直观地观察递归函数执行情况
// 全局变量,记录递归函数的递归层数
int count = 0;
// 输入 n,打印 n 个 tab 缩进
void printIndent(int n) {
for (int i = 0; i < n; i++) {
printf(" ");
}
}
在递归函数的开头,调用 printIndent(count++)
并打印关键变量;然后在所有 return
语句之前调用 printIndent(--count)
并打印返回值。
举个具体的例子,比如说上篇文章 练琴时悟出的一个动态规划算法 中实现了一个递归的 dp
函数,大致的结构如下:
int dp(string& ring, int i, string& key, int j) {
/* base case */
if (j == key.size()) {
return 0;
}
/* 状态转移 */
int res = INT_MAX;
for (int k : charToIndex[key[j]]) {
res = min(res, dp(ring, j, key, i + 1));
}
return res;
}
进行debug,将代码改成(debug的代码被注释了):
int count = 0;
void printIndent(int n) {
for (int i = 0; i < n; i++) {
printf(" ");
}
}
int dp(string& ring, int i, string& key, int j) {
// printIndent(count++);
// printf("i = %d, j = %d\\n", i, j);
if (j == key.size()) {
// printIndent(--count);
// printf("return 0\\n");
return 0;
}
int res = INT_MAX;
for (int k : charToIndex[key[j]]) {
res = min(res, dp(ring, j, key, i + 1));
}
// printIndent(--count);
// printf("return %d\\n", res);
return res;
}
就是在函数开头和所有 return
语句对应的地方加上一些打印代码。
如果去掉注释,执行一个测试用例,输出如下:
这样,我们通过对比对应的缩进就能知道每次递归时输入的关键参数 i, j
的值,以及每次递归调用返回的结果是多少。
最重要的是,这样可以比较直观地看出递归过程,你有没有发现这就是一棵递归树?
前文 动态规划套路详解 说过,理解递归函数最重要的就是画出递归树,这样打印一下,连递归树都不用自己画了,而且还能清晰地看出每次递归的返回值。