控制台打印AVL树-java

控制台打印AVL树-java


【简介】本文展示如何在控制台打印一棵AVL树(即平衡树)。

节点结构

为举例方便,这里使用了最简单的结构,仅包含当前元素、左节点、右节点及高:

public class AvlNode<TreeNode> {

    TreeNode element;
    AvlNode<TreeNode> left;
    AvlNode<TreeNode> right;
    int height; // 叶节点高度为0,根节点高度最大

    public AvlNode(TreeNode element) {
        this(element, null, null);
    }

    public AvlNode(TreeNode element, AvlNode<TreeNode> left, AvlNode<TreeNode> right)         {
        this.element = element;
        this.left = left;
        this.right = right;
        this.height = 0;
    }

    @Override
    public String toString() {
        return "AvlNode{" +
                "element=" + element +
                ", left=" + (left == null ? null : left.element) +
                ", right=" + (right == null ? null : right.element) +
                ", height=" + height +
                '}';
        }
    }
}

public class TreeNode {

    protected int number;

    public TreeNode(int number) {
        this.number = number;
    }

    public int compareTo(@NotNull TreeNode node) {
        if (node == null) {
            throw new RuntimeException("The node can't be null!");
        }
        if (number > node.number) {
            return 1;
        } else if (number == node.number) {
            return 0;
        } else {
            return -1;
        }
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "number=" + number +
                '}';
    }
}

打印AVL树

【打印细则】只打印元素,不打印线条,元素为1~2位数字(不足2位补空格),左叶节点后面空2格(其父节点在上一行占2格),右叶节点后空4格(其父节点的父节点在往前2行,占居中2格)。如有需要,可稍作改造以打印更多的位数。

【打印原理】假设我们的AVL树是完美的理想AVL树,所有叶节点深度相同(对于深度不足的,在缺失的位置使用“空格+N”占位),于是从完美AVL树,可以准确计算得出各个元素前后需要空出的位置。
以下使用 org.junit 做单元测试打印,以及相关方法:

public class PrintTest {

    @Test
    public void print() {
        printTree(root);
    }

    public void printTree(AvlNode<TreeNode> root) {
        if (root == null) {
            System.out.println("N");
            return;
        }
        List<String> nodesList = new ArrayList<>();
        // 在控制台只能逐行打印,因此行数和树的高度相关
        for (int i = 0; i <= root.height; i++) {
            nodesList.add("");
        }
        // 将树的节点信息放入集合中
        pushNodesToList(nodesList, root, 0, true);
        // 逐行打印
        for (int i = 0; i < nodesList.size(); i++) {
            System.out.println(nodesList.get(i));
        }
    }

    private void pushNodesToList(List<String> nodesList, AvlNode<TreeNode> base, int depth, boolean isLeft) {
        if (base == null) {
            // 当前节点为空,则该节点及其子节点均不存在,构造虚拟节点
            pushVirtualNodesToList(nodesList, depth, isLeft);
            return;
        }
        // 不使用真实height,确保和完美AVL树一致
        int height = nodesList.size() - depth - 1;
        if (height < 0) {
            return;
        }
        String lineStr = nodesList.get(depth) 
                + getSpaces(beforeSpaces(height)) 
                + getNumberStr(base) 
                + getSpaces(afterSpaces(height, isLeft));
        nodesList.set(depth, lineStr);
        depth++;
        pushNodesToList(nodesList, base.left, depth, true);
        pushNodesToList(nodesList, base.right, depth, false);
    }

    private void pushVirtualNodesToList(List<String> nodesList, int depth, boolean isLeft) {
        int height = nodesList.size() - depth - 1;
        if (height < 0) {
            return;
        }
        String lineStr = nodesList.get(depth)
                +  getSpaces(beforeSpaces(height))
                + " N"
                + getSpaces(afterSpaces(height, isLeft));
        nodesList.set(depth, lineStr);
        if (height > 0) {
            pushVirtualNodesToList(nodesList, depth + 1, true);
            pushVirtualNodesToList(nodesList, depth + 1, false);
        }
    }

    // 关键算法,计算元素之前的空格数
    private int beforeSpaces(int height) {
        if (height == 0) {
            return 0;
        }
        if (height == 1) {
            return 2;
        }
        if (height > 1) {
            return (int) Math.round(5 * Math.pow(2, height - 1) - 3);
        }
        throw new RuntimeException("height can't be negative");
    }

    // 关键算法,计算元素之后的空格数
    private int afterSpaces(int height, boolean isLeft) {
        if (height == 0) {
            return isLeft ? 2 : 4;
        }
        if (height == 1) {
            return 6;
        }
        if (height > 1) {
            return (int) Math.round(5 * Math.pow(2, height - 1) + 1);
        }
        throw new RuntimeException("height can't be negative");
    }

    // 元素不足两位则空格补位
    private String getNumberStr(AvlNode<TreeNode> avlNode) {
        if (avlNode == null) {
            return " N";
        }
        return (avlNode.element.number < 10 ? " " : "") + avlNode.element.number;
    }

    // 获取指定数量的空格数
    public static String getSpaces(int count) {
        char space = ' ';
        char[] spaces = new char[count];
        for (int i = 0; i < count; i++) {
            spaces[i] = space;
        }
        return new String(spaces);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值