黑白客 发表于 2023-3-27 18:41

如何调试递归程序,有何技巧?

# 前言
正所谓`工欲善其事必先利其器`,
之前的递归调试,我都是全靠脑洞和头发。

先说明我使用的是`idea`调试java编写的leetcode的`岛屿数量        Medium        2023-03-08        191`的时候被单步调试弄得快要疯了才来搜索这个问题的,然后看到回答里面说`打印`,我真的想捶我自己(别笑QAQ,我只是一时没想到)。


---



# 1 效果展示
直接看效果:

```shell
调用了第1层low函数 | 参数 l r 0 0
0 1 1 1 0
1 1 0 1 0
1 1 0 0 0
0 0 0 0 0

        调用了第2层low函数 | 参数 l r 0 1
        0 0 1 1 0
        1 1 0 1 0
        1 1 0 0 0
        0 0 0 0 0

                调用了第3层low函数 | 参数 l r 0 2
                0 0 0 1 0
                1 1 0 1 0
                1 1 0 0 0
                0 0 0 0 0

                        调用了第4层low函数 | 参数 l r 0 3
                        0 0 0 0 0
                        1 1 0 1 0
                        1 1 0 0 0
                        0 0 0 0 0

                                调用了第5层low函数 | 参数 l r 1 3
                                0 0 0 0 0
                                1 1 0 0 0
                                1 1 0 0 0
                                0 0 0 0 0

                                退出了第5层low函数 | 参数 l r 1 3
                                0 0 0 0 0
                                1 1 0 0 0
                                1 1 0 0 0
                                0 0 0 0 0

                        退出了第4层low函数 | 参数 l r 0 3
                        0 0 0 0 0
                        1 1 0 0 0
                        1 1 0 0 0
                        0 0 0 0 0

                退出了第3层low函数 | 参数 l r 0 2
                0 0 0 0 0
                1 1 0 0 0
                1 1 0 0 0
                0 0 0 0 0

                调用了第3层low函数 | 参数 l r 1 1
                0 0 0 0 0
                1 0 0 0 0
                1 1 0 0 0
                0 0 0 0 0

                        调用了第4层low函数 | 参数 l r 1 0
                        0 0 0 0 0
                        0 0 0 0 0
                        1 1 0 0 0
                        0 0 0 0 0

                                调用了第5层low函数 | 参数 l r 2 0
                                0 0 0 0 0
                                0 0 0 0 0
                                0 1 0 0 0
                                0 0 0 0 0

                                        调用了第6层low函数 | 参数 l r 2 1
                                        0 0 0 0 0
                                        0 0 0 0 0
                                        0 0 0 0 0
                                        0 0 0 0 0

                                        退出了第6层low函数 | 参数 l r 2 1
                                        0 0 0 0 0
                                        0 0 0 0 0
                                        0 0 0 0 0
                                        0 0 0 0 0

                                退出了第5层low函数 | 参数 l r 2 0
                                0 0 0 0 0
                                0 0 0 0 0
                                0 0 0 0 0
                                0 0 0 0 0

                        退出了第4层low函数 | 参数 l r 1 0
                        0 0 0 0 0
                        0 0 0 0 0
                        0 0 0 0 0
                        0 0 0 0 0

                退出了第3层low函数 | 参数 l r 1 1
                0 0 0 0 0
                0 0 0 0 0
                0 0 0 0 0
                0 0 0 0 0

        退出了第2层low函数 | 参数 l r 0 1
        0 0 0 0 0
        0 0 0 0 0
        0 0 0 0 0
        0 0 0 0 0

退出了第1层low函数 | 参数 l r 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

1


```


# 2 递归代码

下面是我要调试的递归的代码:

```java

        //输入:grid = [
//["1","1","1","1","0"],
//["1","1","0","1","0"],
//["1","1","0","0","0"],
//["0","0","0","0","0"]
//]

        public void low(int l , int r,Character[][] grid){

                grid = '0' ;

                int length = grid.length;
                int len = grid.length;
                /*右*/
                while( r+1 < length && grid == '1' ){

                        low(l , ++r,grid);
                }


                /*左*/
                while(0< r-1 && grid == '1' ){
                        low(l , r--,grid);
//                        grid = '0';
//                        r--;
                }

                /*上*/
                while(0<l-1 && grid == '1' ){
                        low(l-- , r,grid);
//                        grid = '0';
//                        l--;
                }

                /*下*/
                while(l+1 < len && grid == '1' ){
                        low(++l , r,grid);
//                        grid = '0';
//                        l++;
                }
        }

}

```




# 3 函数头下面输出
首先我在函数头下面输出调用信息:



```java
        System.out.println("调用了low函数 | 参数 l r grid"+l+r+grid);

```

我还想知道`每一个调用函数是第几层`,如果这一大坨输出`有缩进`会不会更好一点呢?缩进简单,输出制表符“\t”就好。但是输出几个呢?

所以需要用一个`计数的变量`来计算目前递归的层数,进而产生缩进效果。有两个方法,一个方法是我敲这些字的时候想到的,`全局变量,在函数头自增1,在函数尾自减1,就可以知道当前递归层数。`


我用的是`静态变量。`对于一个函数里面的静态变量来说,使用static关键字声明并初始化它之后,这条声明语句会在下一次执行时忽略,并且这个静态变量会一直活到程序结束,不像其他函数中声明的局部变量一样命短。其实这也相当于是一种全局变量,只不过是在调用这个函数的时候才创建出来。其它和第一种方法一样,在函数头自增1,在函数尾自减1。

然后在想要缩进的地方前面加上:

```java
staticInteger leve = 0;
```



```java
                String lev = "";
                for (Integer i = 0; i < leve; i++) {
                        lev += "\t";
                }
                System.out.println(lev+"调用了第"+ leve++ +"层low函数 | 参数 l r "+l+" "+r);
                for (int i = 0; i < len; i++) {
                        int length1 = grid.length;
                        System.out.print(lev);
                        for (int i1 = 0; i1 < length1; i1++) {
                                System.out.print(grid+" ");
                        }
                        System.out.println();
                }
                System.out.println();

// 程序代码


                System.out.println(lev+"退出了第"+ leve-- +"层low函数 | 参数 l r "+l+" "+r);
                for (int i = 0; i < len; i++) {
                        int length1 = grid.length;
                        System.out.print(lev);
                        for (int i1 = 0; i1 < length1; i1++) {
                                System.out.print(grid+" ");
                        }
                        System.out.println();
                }
                System.out.println();

```




# 4 调试步骤
分析打印的日志
然后在这里面找到有问题的函数调用,设置参数值的条件断点:

点击打上断点之后,右击断点,输入条件即可。
![](https://img-blog.csdnimg.cn/9643ed226a6f4e5fa33c24849b9d5e14.png)

# 5 完整源码
因为博主在看别的博客的时候,有时看到截取的片段代码很难使用,或者理解。
这里提供操作所用的完整代码仅供参考,有帮助记得点赞收藏哦!

```java
public class P200_NumberOfIslands{
        staticInteger leve = 0;
       public static void main(String[] args) {
              //测试代码
              Solution solution = new P200_NumberOfIslands().new Solution();
                  String data = "[[\"1\",\"1\",\"1\",\"1\",\"0\"],[\"1\",\"1\",\"0\",\"1\",\"0\"],[\"1\",\"1\",\"0\",\"0\",\"0\"],[\"0\",\"0\",\"0\",\"0\",\"0\"]]";
               Character[][] characters = ArrayUtil.StrToCharacterArray(data);

               System.out.println(solution.numIslands(characters));
       }
       
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {

    public int numIslands(Character[][] grid) {


                int len = grid.length;
                int res = 0 ;
                for (int i = 0; i < len; i++) {
                        int l = grid.length;
                        for (int i1 = 0; i1 < l; i1++) {
                                if (grid == '1'){
                                        /**/
                                        low(i, i1, grid,1);
                                        res++;
                                }
                        }
                }

                return res;

    }

        //输入:grid = [
//["1","1","1","1","0"],
//["1","1","0","1","0"],
//["1","1","0","0","0"],
//["0","0","0","0","0"]
//]
        public void low(int y , int x,Character[][] grid,int move){

                int r = x;
                int l = y;

                int length = grid.length;
                int len = grid.length;
                grid = '0' ;

                String lev = "";
                for (Integer i = 0; i < leve; i++) {
                        lev += "\t";
                }
                System.out.println(lev+"调用了第"+ ++leve +"层low函数 | 参数 l r "+l+" "+r);
                for (int i = 0; i < len; i++) {
                        int length1 = grid.length;
                        System.out.print(lev);
                        for (int i1 = 0; i1 < length1; i1++) {
                                System.out.print(grid+" ");
                        }
                        System.out.println();
                }
                System.out.println();


                /*右*/
                while( r+1 < length && grid == '1'&& ! (move == 2) ){
                        low(l , ++r,grid,1);
                }
                r = x;

                /*左*/
                while(0<= r-1 && grid == '1'&& !(move == 1)){
                        low(l , --r,grid,2);
                }
                r = x;

                /*上*/
                while(0<= l-1 && grid == '1'&& !(move == 3)   ){
                        low(--l , r,grid,4);
                }
                l = y;
                /*下*/
                while(l+1 < len && grid == '1'&& !(move == 4)   ){
                        low(++l , r,grid,3);
                }
                l = y;


                System.out.println(lev+"退出了第"+ leve-- +"层low函数 | 参数 l r "+l+" "+r);
                for (int i = 0; i < len; i++) {
                        int length1 = grid.length;
                        System.out.print(lev);
                        for (int i1 = 0; i1 < length1; i1++) {
                                System.out.print(grid+" ");
                        }
                        System.out.println();
                }
                System.out.println();

        }

}
//leetcode submit region end(Prohibit modification and deletion)

}


```

## 6 完成
附上我的AC给大家沾沾喜气吧,虽然耗时和内存消耗还比较高,但是很省头发的调试出来了!也祝大家把所有问题AC!有帮助多多点赞收藏哦。

```shell
解答成功:
        执行耗时:3 ms,击败了62.98% 的Java用户
        内存消耗:49.7 MB,击败了66.20% 的Java用户
```

babala12 发表于 2023-3-27 18:50

好仔细,学,学

dmxayjn 发表于 2023-3-27 21:03

方法很不错,,又学一招
页: [1]
查看完整版本: 如何调试递归程序,有何技巧?