`
xuwenjin666
  • 浏览: 46931 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

(转)如果要用Java实现算法,一定慎用递归

    博客分类:
  • java
 
阅读更多

现象 :

递归是我们很经典的一种算法实现,可以很好的描述一个算法的原理!对于算法的描述、表现和代码结构理解上,递归都是不错的选择!

但是本文想说的是java实现一个递归算法的时候尽量不要用递归实现,而是转换成的非递归实现  。

最近在实现一个比较复杂算法的时候,尝试了一下,非递归实现相比递归实现速度上能提升1/3  。

以下面一个简单的例子来说:(注:为了描述简单,所以这里只用一个简单的例子)

输入参数:N

输出结果: log1+log2+log3+....+logN

两种实现代码如下:

Java代码

 

 

  1. package test;     
  2.     
  3. public class RecursiveTest {     
  4.     /**    
  5.      * 递归实现    
  6.      *     
  7.      * @param n    
  8.      * @return    
  9.      */    
  10.     public static double recursive(long n) {     
  11.         if (n == 1) {     
  12.             return Math.log(1);     
  13.         } else {     
  14.             return Math.log(n) + recursive(n - 1);     
  15.         }     
  16.     }     
  17.     
  18.     /**    
  19.      * 非递归实现    
  20.      *     
  21.      * @param n    
  22.      * @return    
  23.      */    
  24.     public static double directly(long n) {     
  25.         double result = 0;     
  26.         for (int i = 1; i <= n; i++) {     
  27.             result += Math.log(i);     
  28.         }     
  29.         return result;     
  30.     }     
  31.     
  32.     public static void main(String[] args) {     
  33.         int i = 5000000;     
  34.         long test = System.nanoTime();     
  35.         long start1 = System.nanoTime();     
  36.         double r1 = recursive(i);     
  37.         long end1 = System.nanoTime();     
  38.         long start2 = System.nanoTime();     
  39.         double r2 = directly(i);     
  40.         long end2 = System.nanoTime();     
  41.     
  42.         System.out.println("recursive result:" + r1);     
  43.         System.out.println("recursive time used:" + (end1 - start1));     
  44.         System.out.println("non-recursive result:" + r2);     
  45.         System.out.println("non-recursive time used:" + (end2 - start2));     
  46.     }     
  47. }    

 

得到运行结果如下:

 

  1. recursive result:7.212475098340103E7  
  2. recursive time used:539457109   
  3. non-recursive result:7.212475098340103E7  
  4. non-recursive time used:282479757  

 

可以看出递归的运行时间是非递归运行时间将近2倍  。

(注:以上代码还是在-Xss200m的参数下运行的,如果栈空间不足,直接不能运行)

原因简单分析:

 

 

上图是java线程栈的结构  。java将为每个线程维护一个堆栈,堆栈里将为每个方法保存一个栈帧,栈帧代表了一个方法的运行状态  。 也就是我们常说的方法栈  。最后一个为当前运行的栈帧  。

那么每一次方法调用会涉及:

1.为新调用方法的生成一个栈帧

2.保存当前方法的栈帧状态

3.栈帧上下文切换,切换到最新的方法栈帧  。

递归实现将导致在栈内存的消耗(往往需要调整Xss参数)和因为创建栈帧和切换的性能开销,最终大大的影响效率!

所以,如果你想提升你的算法效率,不要使用递归实现是一个基础原则!

另外,递归是我们用来理解算法的一个方法,当用代码来实现的时候基本都可以转换成非递归的代码实现!

分享到:
评论

相关推荐

    Java数据结构和算法(第二版)

    为什么使用二叉树? 树的术语 一个类比 二叉搜索树如何工作 查找节点 插入一个节点 遍历树 查找最大值和最小值 删除节点 二叉树的效率 用数组表示树 重复关键字 完整的tree.java程序 哈夫曼(Huffman)编码 小结 问题 ...

    必会的40个Java代码优化细节.pdf

    7. **慎用`synchronized`**:同步可能导致性能下降,尽量缩小同步范围,使用同步方法代替同步代码块。 8. **避免`finalize`方法**:`finalize`方法执行时机不确定,不利于资源清理。建议使用`try-finally`或`try-...

    Java 50 道面试题及答案.docx

    递归方法简单直观,但需要注意的是,递归算法可能会因为调用栈过深导致栈溢出,尤其在处理大数据量时要慎用。 素数判断问题则考察了面试者对基本算法的理解。素数是只有1和它本身两个因子的自然数。在编写程序判断...

    Java语言中内存管理的几个技巧

    #### 六、慎用`static`变量 `static`变量在整个类的生命周期中只有一份副本,虽然可以共享,但容易成为内存泄漏的源头。除非必要,应优先考虑使用`final`修饰的类内私有常量,这样既能保持不变性,又可避免潜在的...

    突破程序员基本功的16课.part2

    5.5.1 慎用字符的Unicode转义形式 5.5.2 中止行注释的转义字符 5.6 泛型可能引起的错误 5.6.1 原始类型变量的赋值 5.6.2 原始类型带来的擦除 5.6.3 创建泛型数组的陷阱 5.7 正则表达式的陷阱 5.8 多线程的...

    Linux多线程服务端编程:使用muduo C++网络库

    2.1.1只使用非递归的mutex . . . . . . . . . . . . . .. . . . . . . . . . 33 2.1.2死锁. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 35 2.2条件变量(condition variable). . . . . . ....

Global site tag (gtag.js) - Google Analytics