LeetCode算法题-Binary Tree Level Order Traversal II(Java实现)

这是悦乐书的第165次更新,第167篇原创

01 看题和准备

今天介绍的是LeetCode算法题中Easy级别的第24题(顺位题号是107)。给定二叉树,返回其节点值的自下而上级别顺序遍历(即从左到右,逐层逐层)。例如:

给定二叉树[3,9,20,null,null,15,7],

    3
   / \
  9  20
     / \
    15  7

返回其自下而上的级别顺序遍历:[[15,7],[9,20],[3]]。

本次解题使用的开发工具是eclipse,jdk使用的版本是1.8,环境是win7 64位系统,使用Java语言编写和测试。

02 第一种解法

特殊情况:当传入的二叉树为空时,返回我们新定义的空List即可。

正常情况:从示例最后要求输出的结果来看,根节点是数组的最后一位元素,如果是自顶向下遍历节点,我们可以使用队列,借助其先进先出的特点,一层一层遍历节点,将每一层遍历的节点值存入List中,再将List放入List2中,最后再倒序遍历List2存入List3中,List3就是最后的结果。

public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> list2 = new ArrayList<>();
    if (root == null) {
        return list2;
    }
    List<Integer> list = new ArrayList<Integer>();
    Queue<TreeNode> q = new LinkedList<>();
    q.add(root);
    while (!q.isEmpty()) {
        list = new ArrayList<Integer>();
        Queue<TreeNode> tem = new LinkedList<>();
        while (!q.isEmpty()) {
            TreeNode t = q.poll();
            list.add(t.val);
            if (t.left != null) {
                tem.add(t.left);
            }
            if (t.right != null) {
                tem.add(t.right);
            }
        }
        list2.add(list);
        q = tem;
    }
    List<List<Integer>> list3 = new ArrayList<>();
    for(int i=list2.size()-1; i >= 0; i--){
        list3.add(list2.get(i));
    }
    return list3;
}

遍历节点的处理方法和昨天那道求最长路径的写法类似,借助队列先进先出的特点,从左往右依次循环。

03 第二种解法

我们可以将第一种解法再优化下,不再创建新的队列来存新的一层的节点。和昨天那道题的写法类似,借助队列的size来作为判断条件。

public List<List<Integer>> levelOrderBottom2(TreeNode root) {
    List<List<Integer>> list2 = new ArrayList<>();
    if (root == null) {
        return list2;
    }
    List<Integer> list = new ArrayList<Integer>();
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    while (!q.isEmpty()) {
        list = new ArrayList<Integer>();
        int n = q.size();
        while (n-- > 0) {
            TreeNode t = q.poll();
            list.add(t.val);
            if (t.left != null) {
                q.offer(t.left);
            }
            if (t.right != null) {
                q.offer(t.right);
            }
        }
        list2.add(list);
    }
    List<List<Integer>> list3 = new ArrayList<>();
    for(int i=list2.size()-1; i >= 0; i--){
        list3.add(list2.get(i));
    }
    return list3;
}


04 第三种解法

上面两种解法都是使用新的List来存储List2倒序遍历的值作为最后结果返回,既然是先进后出的特点,我们可以使用栈来存储每遍历一层节点存入的List,再使用栈的pop方法出栈存入新的List。

public List<List<Integer>> levelOrderBottom3(TreeNode root) {
    List<List<Integer>> list2 = new ArrayList<>();
    if (root == null) {
        return list2;
    }
    List<Integer> list = new ArrayList<Integer>();
    Stack<List<Integer>> stack = new Stack<List<Integer>>();
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    while (!q.isEmpty()) {
        list = new ArrayList<Integer>();
        int n = q.size();
        while (n-- > 0) {
            TreeNode t = q.poll();
            list.add(t.val);
            if (t.left != null) {
                q.offer(t.left);
            }
            if (t.right != null) {
                q.offer(t.right);
            }
        }
        stack.add(list);
    }
    while (!stack.isEmpty()) {
        list2.add(stack.pop());
    }
    return list2;
}


05 第四种解法

上面的三种都是使用遍历的方式,那我们可不可以使用递归的方法遍历每一层的节点值?显然是可以的,我们可以使用层数作为标记,来判断现阶段处于那一层,从而来决定是新建一个List还是取已存在的List往里面存入新的节点值。

public List<List<Integer>> levelOrderBottom4(TreeNode root) {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    List<List<Integer>> node = new ArrayList<List<Integer>>();
    viewTree(root, res, 0);
    for (int i=res.size()-1; i>=0; i--) {
        node.add(res.get(i));
    }
    return node;
}

public void viewTree(TreeNode root, List<List<Integer>> res, int deep) {
    if(root == null)
        return;
    if (res.size() <= deep) {
        List<Integer> node = new ArrayList<Integer>();
        node.add(root.val);
        res.add(node);   
    } else {
        List<Integer> node = (List<Integer>)res.get(deep);
        node.add(root.val);
    }
    viewTree(root.left, res, deep+1);
    viewTree(root.right, res, deep+1);
}

从根节点开始,先递归获取根节点左子节点及其子节点的节点值,然后再递归获取根节点右子节点及其子节点的节点值。

06 验证与测试

对于上面四种解法,我们选取了一个四层的二叉树来作为测试数据,测试代码如下:

public static void main(String[] args) {
    Easy_107_BinaryTreeLevelOrderTraversalII instance = new Easy_107_BinaryTreeLevelOrderTraversalII();
    TreeNode t = new TreeNode(1);
    TreeNode t2 = new TreeNode(2);
    TreeNode t3 = new TreeNode(3);
    TreeNode t4 = new TreeNode(4);
    TreeNode t5 = new TreeNode(5);
    TreeNode t6 = new TreeNode(6);
    TreeNode t7 = new TreeNode(7);
    TreeNode t8 = new TreeNode(8);
    t.left = t2;
    t.right = t3;
    t2.left = t4;
    t2.right = t5;
    t3.left = t6;
    t3.right = t7;
    t7.left = t8;
    long start = System.nanoTime();
    List<List<Integer>> result = instance.levelOrderBottom(t);
    long end = System.nanoTime();
    System.out.println("levelOrderBottom---输出:"+result.toString()+" , 用时:"+(end-start)/1000+"微秒");
    long start2 = System.nanoTime();
    List<List<Integer>> result2 = instance.levelOrderBottom2(t);
    long end2 = System.nanoTime();
    System.out.println("levelOrderBottom2---输出:"+result2.toString()+" , 用时:"+(end2-start2)/1000+"微秒");
    long start3 = System.nanoTime();
    List<List<Integer>> result3 = instance.levelOrderBottom3(t);
    long end3 = System.nanoTime();
    System.out.println("levelOrderBottom3---输出:"+result3.toString()+" , 用时:"+(end3-start3)/1000+"微秒");
    long start4 = System.nanoTime();
    List<List<Integer>> result4 = instance.levelOrderBottom4(t);
    long end4 = System.nanoTime();
    System.out.println("levelOrderBottom4---输出:"+result4.toString()+" , 用时:"+(end4-start4)/1000+"微秒");
}

测试结果如下:

levelOrderBottom---输出:[[8], [4, 5, 6, 7], [2, 3], [1]] , 用时:446微秒
levelOrderBottom2---输出:[[8], [4, 5, 6, 7], [2, 3], [1]] , 用时:24微秒
levelOrderBottom3---输出:[[8], [4, 5, 6, 7], [2, 3], [1]] , 用时:55微秒
levelOrderBottom4---输出:[[8], [4, 5, 6, 7], [2, 3], [1]] , 用时:23微秒

因为只是采用单一数据测试,样本数量过少,无法得出太过准确的结论,但还是可以明显看出解法二和解法四其他条件一样的情况下用时是最少的。

07 小结

以上就是全部内容,如果大家有什么好的解法思路、建议或者其他问题,可以下方留言交流,点赞、留言、转发就是对我最大的回报和支持!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容