一次递归带来的的stackOverFlow

来源:互联网 发布:mysql navicat 破解版 编辑:程序博客网 时间:2024/06/01 11:36

现象描述

刚才正在写算法题,运行后,看到了一大堆的红色的控制台输出,以为是程序出现了“索引超出”异常,仔细一看,却是Exception in thread "main" java.lang.StackOverflowError.

enter description here

我的算法代码是

public static void main(String[] args) {        int[][]arr={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};        print(arr,0,0,arr.length-1,arr[0].length-1);    }    public static void print(int[][] arr,int x1, int y1, int x2, int y2 ) {        for(int i=x1;i<x2;i++) {            System.out.print(arr[y1][i]+" ");        }        for(int i=y1;i<y2;i++){            System.out.print(arr[i][x2]+" ");        }        for(int i=x2;i>x1;i--){            System.out.print(arr[y2][i]+" ");        }        for(int i=y2;i>y1;i--){            System.out.print(arr[i][x1]+" ");        }        print(arr, x1+1, y1+1, x2-1, y2-1);    }

Exception in thread "main" java.lang.StackOverflowError出现的原因

这种情况的出现可能是eclipse智障了吧(平时项目用的都是idea,只有在做算法题目时偶尔用eclipse),那我就把这种情况当成是项目中出现的问题吧,从头来解决一下,毕竟在面试的时候,就有面试官问道了递归算法可能会带来什么问题,就是StackOverflowErro咯。

解决步骤1:检查是不是代码不能正常结束

修改后的main方法如下面的代码所示:

public static void main(String[] args) {        int[][]arr={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};        print(arr,0,0,arr.length-1,arr[0].length-1);        System.out.println("程序可以输出这句话,因为此程序可以正常结束");}

执行,看控制台输出

enter description here

发现真的可能是我的问题,我程序中用到了递归了。。。。。。。。。。那应该是我们的递归没有终止条件咯吧。。。

解决步骤2:修改print方法

经过分析,我发现我在原来的print方法最后的地柜调用中,没有判断x2<x1以及y2<y1.那在print方法的头部进行判断,修改后的代码如下所示:

public static void print(int[][] arr,int x1, int y1, int x2, int y2 ) {        if(x2<x1 ||y2<y1 ){            return;        }        for(int i=x1;i<x2;i++) {            System.out.print(arr[y1][i]+" ");        }        for(int i=y1;i<y2;i++){            System.out.print(arr[i][x2]+" ");        }        for(int i=x2;i>x1;i--){            System.out.print(arr[y2][i]+" ");        }        for(int i=y2;i>y1;i--){            System.out.print(arr[i][x1]+" ");        }        print(arr, x1+1, y1+1, x2-1, y2-1);}

程序运行结果如下图所示:

enter description here

可以看到现在程序是可以正常结束的了。

继续扯:如何将递归程序编写非递归

可以看到上面的print方法在自身的最后一行代码调用了自身,这是一种尾递归的写法。上次阿里巴巴的老师问我是不是所有的地柜都可以用非递归的方法来实现,我回答可以,然后老师就让我了解一下尾递归,我当时以为是伪递归。。。

什么是伪递归

顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数,深层函数所面对的不是越来越简单的问题,而是越来越复杂的问题,因为参数里带有前面若干步的运算路径。

尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。比如

f(n, sum) = f(n-1) + value(n) + sum; 

会保存n个函数调用堆栈,而使用尾递归

f(n, sum) = f(n-1, sum+value(n)); 

这样则只保留后一个函数堆栈即可,之前的可优化删去。

关于所有的尾递归是否都可以用非递归的方法来实现,我目前还是没有找到一个准确的答案,下面给一个知乎上轮子哥(vczh)参与的一个讨论吧,地址如下:enter description here

那上面的代码我还是用非递归来实现一下吧

public static void printUnRec(int[][] arr) {        int x1 = 0, y1 = 0, x2 = arr.length - 1, y2 = arr[0].length - 1;        while (x2 >=x1 && y2 >= y1) {            for (int i = x1; i < x2; i++) {                System.out.print(arr[y1][i] + " ");            }            for (int i = y1; i < y2; i++) {                System.out.print(arr[i][x2] + " ");            }            for (int i = x2; i > x1; i--) {                System.out.print(arr[y2][i] + " ");            }            for (int i = y2; i > y1; i--) {                System.out.print(arr[i][x1] + " ");            }            x1++;            y1++;            x2--;            y2--;        }}

程序的运行结果

enter description here

嗯,是我智障了,不是eclipse智障。

那我们在写递归算法的时候,是不是要仔细检查递归终止条件呢。。。

0 0
原创粉丝点击