程序员代码面试指南第二版 33.打印二叉树的边界节点

welcome to my blog

程序员代码面试指南第二版 33.打印二叉树的边界节点

题目描述
给定一颗二叉树的根节点 root,按照如下两种标准分别实现二叉树的边界节点的逆时针打印。

标准一:
1,根节点为边界节点。
2,叶节点为边界节点。
3,如果节点在其所在的层中是最左的或最右的,那么该节点也是边界节点。

标准二:
1,根节点为边界节点。
2,叶节点为边界节点。
3,树左边界延伸下去的路径为边界节点。
4,树右边界延伸下去的路径为边界节点。
ps:具体请对照样例

输入描述:
第一行输入两个整数 n 和 root,n 表示二叉树的总节点个数,root 表示二叉树的根节点。
以下 n 行每行三个整数 fa,lch,rch,表示 fa 的左儿子为 lch,右儿子为 rch。(如果 lch 为 0 则表示 fa 没有左儿子,rch同理)

输出描述:
输出两行整数分别表示按两种标准的边界节点。

示例1

输入
16 1
1 2 3
2 0 4
4 7 8
7 0 0
8 0 11
11 13 14
13 0 0
14 0 0
3 5 6
5 9 10
10 0 0
9 12 0
12 15 16
15 0 0
16 0 0
6 0 0

输出
1 2 4 7 11 13 14 15 16 12 10 6 3
1 2 4 7 13 14 15 16 10 6 3
第一次做; 如何找到树中每一层的最左节点和最右节点; 前序遍历和后续遍历要掌握牢固!; 超时,通过75%
/*
标准一:前序遍历
标准二:打印左边界时前序遍历; 打印右边界时后续遍历; 标准二下, 如果当前节点是父节点的右孩子, 那么只有当父节点是爷爷节点的左孩子时, 才能打印当前节点
*/
import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String[] str = sc.nextLine().split(" ");
        int n = Integer.parseInt(str[0]);
        int rootVal = Integer.parseInt(str[1]);
        TreeNode[] nodes =  new TreeNode[n+1];
        //初始化, 在堆中开辟空间
        for(int i=1; i<=n; i++){
            nodes[i] = new TreeNode(i);
        }
        //i仅表示读取次数, 没有实际含义
        for(int i=1; i<=n; i++){
            str = sc.nextLine().split(" ");
            int val = Integer.parseInt(str[0]);
            int left = Integer.parseInt(str[1]);
            int right = Integer.parseInt(str[2]);
            nodes[val].left = left==0?null:nodes[left];
            nodes[val].right = right==0?null:nodes[right];
        }
        TreeNode root = nodes[rootVal];
        //
        int height = getHeight(root);
        TreeNode[][] levelLR = new TreeNode[height][2];
        getLevelLR(root, levelLR, 0);
        printEdge1(levelLR);
        System.out.println();
        printEdge2(root);
    }
    //获取树的高度
    public static int getHeight(TreeNode root){
        if(root==null)
            return 0;
        return Math.max(getHeight(root.left), getHeight(root.right))+1;
    }
    //获取每一层最左最右的节点;前序遍历;
    //核心:前序遍历中, 第i层的情况: 第一次碰到的就是最左的节点, 最后一次碰到的是最右节点; 也就是说每到第i层, 都要更新最右节点, 最左节点不动
    //寻找最右和最右节点的思想很重要! 需要掌握; 这个思想建立在二叉树的前序遍历基础上
    public static void getLevelLR(TreeNode root, TreeNode[][] levelMap, int level){
        if(root==null)
            return;
        //第i层的最左节点
        levelMap[level][0] = levelMap[level][0]==null?root:levelMap[level][0];
        //第i层的最右节点
        levelMap[level][1] = root;
        //新条件新递归
        getLevelLR(root.left, levelMap, level+1);
        getLevelLR(root.right, levelMap, level+1);
    }
    ///
    //按标准一打印
    //levelLR表示level层的最左最右节点引用; 其中,levelLR[i][0]表示第i层的最左节点, level[i][1]表示第i层的最右节点; i从0开始
    public static void printEdge1(TreeNode[][] levelLR){
        if(levelLR==null||levelLR.length==0)
            return;
        //print root val
        System.out.print(levelLR[0][0].val+" ");
        //从上到下打印第i层的最左节点
        for(int i=1; i<levelLR.length; i++){
            System.out.print(levelLR[i][0].val+" ");
        }
        //打印叶子节点,并且该叶子节点不是当前层的最左节点同时也不是当前层的最右节点
        printNormalLeaf(levelLR[0][0], levelLR, 0);
        //从下到上打印第i层的最右节点; 注意不打印根节点所在的层
        for(int i=levelLR.length-1; i>=1; i--){
            //细节: 打印右边界时要注意检查, 当前节点是不是也是左边界! 是的话就不能打印了, 因为在打印左边界时已经打印了, 再打印就重复了
            if(levelLR[i][0]!=levelLR[i][1])
                System.out.print(levelLR[i][1].val+" ");
        }
    }
    public static void printNormalLeaf(TreeNode root, TreeNode[][] levelLR, int level){
        if(root==null)
            return;
        //叶子节点不是只在最后一层!!! 清醒点
        if(root.left==null && root.right==null && root!=levelLR[level][0] && root!=levelLR[level][1]){
            System.out.print(root.val+" ");
        }
        //新条件新递归
        printNormalLeaf(root.left, levelLR, level+1);
        printNormalLeaf(root.right, levelLR, level+1);
    }
    ///
    //按标准二打印
    //先找到第一个既有左孩子又有右孩子的节点
    public static void printEdge2(TreeNode root){
        if(root==null)
            return;
        //先打印
        System.out.print(root.val+" ");
        //当root有左孩子和右孩子时; 进入到if内部后, 就不会再发生printEdge2的递归调用了
        if(root.left!=null && root.right!=null){
            printLeftEdge(root.left, true);
            printRightEdge(root.right, true);
        }
        //当root孩子不全时, 新条件新递归
        else{
            printEdge2(root.left!=null?root.left:root.right);
        }
    }
    //根左右; 从上往下打印; 打印的条件:要么print==true, 要么root.right没有兄弟节点
    public static void printLeftEdge(TreeNode root, boolean print){
        if(root==null)
            return;
        if(print || (root.left==null && root.right==null)){
            System.out.print(root.val+" ");
        }
        printLeftEdge(root.left, print);
        //
        printLeftEdge(root.right, print && root.left==null?true:false);
    }
    //左右根; 从下往上打印; 打印的条件:要么print==true, 要么root.left没有兄弟节点
    public static void printRightEdge(TreeNode root, boolean print){
        if(root==null)
            return;
        printRightEdge(root.left, print && root.right==null?true:false);
        printRightEdge(root.right, print);
        //细节: print表示非叶子节点是否可以打印; 叶子节点需要单独判断; 也就是把节点分成非叶子节点和叶子节点两类进行讨论
        if(print || (root.left==null && root.right==null)){
            System.out.print(root.val+" ");
        }
    }
    public static class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int val){
            this.val = val;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值