Leetcode: Binary Tree Paths 从一道Easy题看Java String链接效率

别说话,快上题!

Given a binary tree, return all root-to-leaf paths.

For example, given the following binary tree:

   1
 /   \
2     3
 \
  5

All root-to-leaf paths are:

["1->2->5", "1->3"]

其实这是一道挺简单的Tree的DFS输出路径的题目,但是题目唯一tricky的地方就是输出。它是要输出一个String类型的list,并不是像往常一样输出一个Integer类型的Array。其实就是一个String的链接嘛。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> Result = new ArrayList();
        
        if(root == null){
            return Result;
        }
        
        
        String temp = new String();
        pathHelper(root,temp,Result);
        
        
        return Result;
        
        
    }
    private void pathHelper(TreeNode root,
                            String temp,
                            List<String> Result)
    {
        if (root == null){
            return;
        }
        
        
        temp += String.valueOf(root.val);
        int length = temp.length();
        
        
        if (root.left == null && root.right == null){
            Result.add(temp);
            return;
        }
        
        
        pathHelper(root.left,temp + "->",Result);
        pathHelper(root.right,temp + "->",Result);
        temp.substring(0,length-1);
    }
                                
                            
}


 最后一行是为了确保返递归返回时把上一个解去掉,这样才能找到所有的解。 

其实这道题讲究的地方还是挺多的。如果在面试的时候给了这样的解答,会有一个follow up就是如何提高string链接的效率。其实在Java中,String的‘+’操作符是javaJDK中唯一overload的操作符。它其实是完成了append()方法,所以使用‘+’操作符进行字符串链接时效率并没有那么高。

在Thinking in Java的String那章里,作者专门对String的‘+’操作符与StringBuilder中的append()方法的效率进行了一个比较。

public class Concatenation {
      public static void main(String[] args) {
        String mango = "mango";
        String s = "abc" + mango + "def" + 47;
        System.out.println(s);
      }
    } /* Output:
    abcmangodef47
    *///:~
这是作者进行试验的代码。将三个字符串进行链接。使用java JDK中的javap工具我们可以查看JVM在编译这段代码时做了什么

javap -c Concatenation
在command line中输入上面命令  -c 命令会产生JVM编译时候的字节码。跳过一些不重要部分我们可以截取最关键的部分:

public static void main(java.lang.String[]);
      Code:
       Stack=2, Locals=3, Args_size=1
       0:    ldc #2; //String mango
	2:    astore_1
   	3:    new #3; //class StringBuilder
   	6:    dup
   	7:    invokespecial #4; //StringBuilder."<init>":()
   	10:   ldc #5; // String abc
   	12    invokevirtual #6; //StringBuilder.append:(String)
   	15    aload_1
   	16    invokevirtual #6; //StringBuilder.append:(String)
   	19    ldc #7; //String def
   	21    invokevirtual #6; //StringBuilder.append:(String)
   	24    bipush 47
   	26    invokevirtual #8; //StringBuilder.append:(I)
   	29    invokevirtual #9; //StringBuilder.toString:()
   	32    astore_2
   	33    getstatic #10; //Field System.out:PrintStream;
   	36    aload_2
   	37    invokevirtual #11; // PrintStream.println:(String)
   	40    return

line 7 12 16 21 26 29我们可以看到 通过invokevirtual命令,编译器调用了JVM中的StringBuilder这个类,但是我们的代码中并没有使用这个类,但是编译器决定这么做,应为StringBuilder的效率更高。


我们再将实验代码换成StringBuilder实现:

//: strings/WhitherStringBuilder.java
public class WhitherStringBuilder {
  public String implicit(String[] fields) {
    String result = "";
    for(int i = 0; i < fields.length; i++)
      result += fields[i];
    return result;
  }
  public String explicit(String[] fields) {
    StringBuilder result = new StringBuilder();
    for(int i = 0; i < fields.length; i++)
      result.append(fields[i]);
    return result.toString();

}
} ///:~ 


我们使用Javap -c WithStringBuilder 我们可以比较两个方法的不同之处:

第一个是使用+运算符的。

public java.lang.String implicit(java.lang.String[]);
  Code:
   0:    ldc #2; //String
   2:    astore_2
   3:    iconst_0
   4:    istore_3
   5:    iload_3
   6:    aload_1
   7:    arraylength
   8:    if_icmpge 38
   11:   new #3; //class StringBuilder
   14:   dup
   15:   invokespecial #4; // StringBuilder.”<init>”:()
   18:   aload_2
   19:   invokevirtual #5; // StringBuilder.append:()
   22:   aload_1
   23    iload_3
   24    aaload
   25:   invokevirtual #5; // StringBuilder.append:()
   28:   invokevirtual #6; // StringBuiIder.toString:()
   31:   astore_2
   32:   iinc 3, 1
   35:   goto 5
   38:   aload_2
   39    areturn

接下来是使用StringBuilder的:

public java.lang.String explicit(java.lang.String[]);
  Code:
   0:    new #3; //class StringBuilder
   3:    dup
   4:    invokespecial #4; // StringBuilder.”<init>”:()
   7:    astore_2
   8:    iconst_0
   9:    istore_3
   10:   iload_3
   11:   aload_1
   12:   arraylength
   13:   if_icmpge 30
   16:   aload_2
   17:   aload_1
   18:   iload_3
   19:   aaload
   20    invokevirtual #5; // StringBuilder.append:()
   23    pop
   24:   iinc 3,1
   27:   goto 10
   30:   aload_2
   31:   invokevirtual #6; // StringBuiIder.toString:()
   34:   areturn

可以看到第二个方法,JVM以及编译器会提升很多效率。

在@tianping168博主的文章:http://blog.csdn.net/tianping168/article/details/2458103 中有对于Java各个String链接方式的性能测试。大家有兴趣的可以去看一看。


最后贴一下这道题使用StringBuilder的方法:


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> Result = new ArrayList();
        
        
        if (root == null){
            return Result;
        }
        StringBuilder temp = new StringBuilder();
        minHelper(root,temp,Result);
        return Result;
    }
    
    private void minHelper(TreeNode  root,
                           StringBuilder temp,
                           List<String> Result){
        
        if(root == null){
            return; 
        }
        
        temp.append(String.valueOf(root.val));
        int length = temp.length();
        if(root.left == null && root.right == null){
           Result.add(temp.toString());
           return;
        }
        
        
       if(root.left != null){
           minHelper(root.left,temp.append("->"),Result);
           temp.delete(length,temp.length());
       }
       if(root.right != null){
           minHelper(root.right,temp.append("->"),Result);
           temp.delete(length,temp.length());
       }
        
        
    }
}

贴个测试时间图 哈哈哈



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值