Java使用层序遍历以特定格式打印二叉树

17

好的,我已经阅读了所有其他相关问题,但没有找到一个能够帮助解决Java问题的。从其他语言中解密出一般的想法;但我还没有弄清楚。

问题:我想进行水平排序(使用递归已经可以工作),并以树的一般形状打印出来。

比如说我有这个:

    1 
   / \
  2   3
 /   / \
4   5   6

我的代码会按照以下方式打印出层级顺序:

1 2 3 4 5 6

我想要像这样打印出来:
1
2 3
4 5 6

在你给我关于完成工作的道德演讲之前...我已经完成了我的AP计算机科学项目,并且当我的老师提到广度优先搜索时,我变得好奇。

我不知道这是否有帮助,但这是我目前的代码:

/**
  * Calls the levelOrder helper method and prints out in levelOrder.
  */
 public void levelOrder()
 {
  q = new QueueList();
  treeHeight = height();
  levelOrder(myRoot, q, myLevel);
 }

 /**
  * Helper method that uses recursion to print out the tree in 
  * levelOrder
  */
 private void levelOrder(TreeNode root, QueueList q, int curLev)
 {
  System.out.print(curLev);
  if(root == null)
  {
   return;
  }

  if(q.isEmpty())
  {
   System.out.println(root.getValue());
  }
  else
  {
   System.out.print((String)q.dequeue()+", ");
  }

  if(root.getLeft() != null)
  {
   q.enqueue(root.getLeft().getValue());
   System.out.println();
  }
  if(root.getRight() != null)
  {
   q.enqueue(root.getRight().getValue());
   System.out.println();
   curLev++;
  }

  levelOrder(root.getLeft(),q, curLev);
  levelOrder(root.getRight(),q, curLev);
 }

从我所了解的,我需要使用树的总高度,并使用一个级别计数器...唯一的问题是我的级别计数器在我的levelOrder使用递归回到树时会继续计数。
抱歉如果这太多了,但一些技巧会很不错。 :)
23个回答

30

以下是代码。这个问题在面试中问到过我...

public void printTree(TreeNode tmpRoot) {

        Queue<TreeNode> currentLevel = new LinkedList<TreeNode>();
        Queue<TreeNode> nextLevel = new LinkedList<TreeNode>();

        currentLevel.add(tmpRoot);

        while (!currentLevel.isEmpty()) {
            Iterator<TreeNode> iter = currentLevel.iterator();
            while (iter.hasNext()) {
                TreeNode currentNode = iter.next();
                if (currentNode.left != null) {
                    nextLevel.add(currentNode.left);
                }
                if (currentNode.right != null) {
                    nextLevel.add(currentNode.right);
                }
                System.out.print(currentNode.value + " ");
            }
            System.out.println();
            currentLevel = nextLevel;
            nextLevel = new LinkedList<TreeNode>();

        }

    }

13

这是最简单的解决方案

public void byLevel(Node root){
     Queue<Node> level  = new LinkedList<>();
     level.add(root);
     while(!level.isEmpty()){
         Node node = level.poll();
         System.out.print(node.item + " ");
         if(node.leftChild!= null)
         level.add(node.leftChild);
         if(node.rightChild!= null)
         level.add(node.rightChild);
     }
}

https://github.com/camluca/Samples/blob/master/Tree.java 在我的 GitHub 上,您可以找到类 Tree 中的其他有用函数,例如:

显示树

****......................................................****
                            42
            25                              65                              
    12              37              43              87              
9      13      30      --      --      --      --      99      
****......................................................****
Inorder traversal
9 12 13 25 30 37 42 43 65 87 99  
Preorder traversal
42 25 12 9 13 37 30 65 43 87 99  
Postorder traversal
9 13 12 30 37 25 43 99 87 65 42  
By Level
42 25 65 12 37 43 87 9 13 30 99  

15
你没有在解决方案中打印任何换行符。 - everconfusedGuy
4
需要进行水平打印,不需要进行树的遍历。 - Rohit
如何获得此输出。无解决方案可能。 - Hutashan Chandrakar

8
以下是我的做法:
levelOrder(List<TreeNode> n) {
    List<TreeNode> next = new List<TreeNode>();
    foreach(TreeNode t : n) {
        print(t);
        next.Add(t.left);
        next.Add(t.right);
    }
    println();
    levelOrder(next);
}

(本来是真实的代码 - 在中途有些无聊,所以变成了伪代码)


3
你为什么不尝试一下并亲身体验呢? - Anon.
它会像这样打印:1 2 34 56 - JavaFail
1
不,没有任何东西阻止算法适用于任意深度。如果它对你不起作用,我会怀疑你实现得不正确。 - Anon.
那我肯定做过了;不幸的是,它本应该对解决我当前的作业有用。而且既然这是额外的工作,我选择去做了,所以我的老师也不会帮我。 - JavaFail
1
递归中没有退出情况! - Luigi Massa Gallerano
显示剩余8条评论

5

刚想分享Anon的建议,提供真正的Java代码,并修复了一些关键问题(例如递归没有结束条件,因此它永远不会停止添加到堆栈中,而且在接收到的数组中不检查null会导致空指针异常)。

另外,像Eric Hauser建议的那样,没有异常,因为它没有修改正在循环遍历的集合,而是修改了一个新集合。

下面是代码:

public void levelOrder(List<TreeNode> n) {
    List<TreeNode> next = new ArrayList<TreeNode>();
    for (TreeNode t : n) {
        if (t != null) {
            System.out.print(t.getValue());
            next.add(t.getLeftChild());
            next.add(t.getRightChild());
        }
    }
    System.out.println();
    if(next.size() > 0)levelOrder(next);
}

2
下面的方法返回一个包含所有节点按层级排列的ArrayList的ArrayList:
 public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {

    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); 
    if(root == null) return result;
    Queue q1 = new LinkedList();
    Queue q2 = new LinkedList();

    ArrayList<Integer> list = new ArrayList<Integer>();
    q1.add(root);

    while(!q1.isEmpty() || !q2.isEmpty()){

        while(!q1.isEmpty()){
            TreeNode temp = (TreeNode)q1.poll();
            list.add(temp.val);
            if(temp.left != null) q2.add(temp.left);
            if(temp.right != null) q2.add(temp.right);
        }
        if(list.size() > 0)result.add(new ArrayList<Integer>(list));
        list.clear();
        while(!q2.isEmpty()){
            TreeNode temp = (TreeNode)q2.poll();
            list.add(temp.val);
            if(temp.left != null) q1.add(temp.left);
            if(temp.right != null) q1.add(temp.right);
        }
        if(list.size() > 0)result.add(new ArrayList<Integer>(list));
        list.clear();
    }
    return result;
}

1

我非常喜欢Anon代码的简洁和优雅。但是,有时候优雅的代码并不总是能够直观地被理解。因此,这里是我的尝试,展示一种类似的方法,需要Log(n)更多的空间,但对于那些最熟悉深度优先搜索(沿树的长度向下)的人来说,应该更容易理解。

以下代码片段将属于特定级别的节点设置为列表,并将该列表排列在包含树所有级别的列表中。因此,您将在下面看到List<List<BinaryNode<T>>>。其余部分应该相当容易理解。

public static final <T extends Comparable<T>> void printTreeInLevelOrder(
        BinaryTree<T> tree) {
    BinaryNode<T> root = tree.getRoot();
    List<List<BinaryNode<T>>> levels = new ArrayList<List<BinaryNode<T>>>();
    addNodesToLevels(root, levels, 0);
    for(List<BinaryNode<T>> level: levels){
        for(BinaryNode<T> node: level){
            System.out.print(node+ " ");
        }
        System.out.println();
    }
}

private static final <T extends Comparable<T>> void addNodesToLevels(
        BinaryNode<T> node, List<List<BinaryNode<T>>> levels, int level) {
    if(null == node){
        return;
    }

    List<BinaryNode<T>> levelNodes;
    if(levels.size() == level){
        levelNodes = new ArrayList<BinaryNode<T>>();
        levels.add(level, levelNodes);
    }
    else{
        levelNodes = levels.get(level);
    }

    levelNodes.add(node);
    addNodesToLevels(node.getLeftChild(), levels, level+1);
    addNodesToLevels(node.getRightChild(), levels, level+1);
}

1

A - 解决方案

我在这里写了直接的解决方案。如果您想要详细的答案、演示代码和解释,请跳过并查看答案的其余标题;

public static <T> void printLevelOrder(TreeNode<T> root) {
    System.out.println("Tree;");
    System.out.println("*****");

    // null check
    if(root == null) {
        System.out.printf(" Empty\n");
        return;
    }

    MyQueue<TreeNode<T>> queue = new MyQueue<>();
    queue.enqueue(root);

    while(!queue.isEmpty()) {
        handleLevel(queue);
    }
}

// process each level
private static <T> void handleLevel(MyQueue<TreeNode<T>> queue) {
    int size = queue.size();

    for(int i = 0; i < size; i++) {
        TreeNode<T> temp = queue.dequeue();
        System.out.printf("%s ", temp.data);
        queue.enqueue(temp.left);
        queue.enqueue(temp.right);
    }

    System.out.printf("\n");
}

B - 说明

为了按层次打印树,您应该使用简单的队列实现来处理每个级别。在我的演示中,我编写了一个非常简约的简单队列类,称为MyQueue

公共方法printLevelOrder将以参数形式接受TreeNode<T>对象实例root,它代表树的根。私有方法handleLevelMyQueue实例作为参数。

在每个级别上,handleLevel方法出队队列,直到队列大小等于该级别的元素数量,然后将换行符放入输出中以控制级别限制。

C - TreeNode类

public class TreeNode<T> {

    T data;
    TreeNode<T> left;
    TreeNode<T> right;

    public TreeNode(T data) {
        this.data = data;;
    }

}

D - MyQueue类:一个简单的队列实现
public class MyQueue<T> {

    private static class Node<T> {

        T data;
        Node next;

        public Node(T data) {
            this(data, null);
        }

        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }

    }

    private Node head;
    private Node tail;
    private int size;

    public MyQueue() {
        head = null;
        tail = null;
    }

    public int size() {
        return size;
    }

    public void enqueue(T data) {
        if(data == null)
            return;

        if(head == null)
            head = tail = new Node(data);
        else {
            tail.next = new Node(data);
            tail = tail.next;
        }

        size++;
    }

    public T dequeue() {

        if(tail != null) {
            T temp = (T) head.data;
            head = head.next;

            size--;

            return temp;
        }

        return null;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void printQueue() {
        System.out.println("Queue: ");
        if(head == null)
            return;
        else {
            Node<T> temp = head;
            while(temp != null) {
                System.out.printf("%s ", temp.data);
                temp = temp.next;
            }
        }
        System.out.printf("%n");
    }
}

E - DEMO:按层次顺序打印树

public class LevelOrderPrintDemo {

    public static void main(String[] args) {
        // root level
        TreeNode<Integer> root = new TreeNode<>(1);

        // level 1
        root.left           = new TreeNode<>(2);
        root.right          = new TreeNode<>(3);

        // level 2
        root.left.left      = new TreeNode<>(4);

        root.right.left     = new TreeNode<>(5);
        root.right.right    = new TreeNode<>(6);

        /*
         *      1      root
         *     / \
         *    2   3    level-1
         *   /   / \
         *  4   5   6  level-2
         */

        printLevelOrder(root);
    }

    public static <T> void printLevelOrder(TreeNode<T> root) {
        System.out.println("Tree;");
        System.out.println("*****");

        // null check
        if(root == null) {
            System.out.printf(" Empty\n");
            return;
        }

        MyQueue<TreeNode<T>> queue = new MyQueue<>();
        queue.enqueue(root);

        while(!queue.isEmpty()) {
            handleLevel(queue);
        }
    }

    // process each level
    private static <T> void handleLevel(MyQueue<TreeNode<T>> queue) {
        int size = queue.size();

        for(int i = 0; i < size; i++) {
            TreeNode<T> temp = queue.dequeue();
            System.out.printf("%s ", temp.data);
            queue.enqueue(temp.left);
            queue.enqueue(temp.right);
        }

        System.out.printf("\n");
    }

}

F - 样例输入

    1      // root
   / \
  2   3    // level-1
 /   / \
4   5   6  // level-2

G - 样例输出

Tree;
*****
1 
2 3 
4 5 6 

1
public class PrintATreeLevelByLevel {
public static class Node{
    int data;
    public Node left;
    public Node right;

    public Node(int data){
        this.data = data;
        this.left = null;
        this.right = null;

    }
}

public void printATreeLevelByLevel(Node n){
    Queue<Node> queue =  new LinkedList<Node>();
    queue.add(n);
    int node = 1; //because at root
    int child = 0; //initialize it with 0 
    while(queue.size() != 0){
        Node n1 = queue.remove();
        node--;
        System.err.print(n1.data +" ");

        if(n1.left !=null){
            queue.add(n1.left);
            child ++;
        }
        if(n1.right != null){
            queue.add(n1.right);
            child ++;
        }
        if( node == 0){
            System.err.println();
            node = child ;
            child = 0;
        }

    }


}

public static void main(String[]args){
    PrintATreeLevelByLevel obj = new PrintATreeLevelByLevel();
    Node node1 = new Node(1);
    Node node2 = new Node(2);
    Node node3 = new Node(3);
    Node node4 = new Node(4);
    Node node5 = new Node(5);
    Node node6 = new Node(6);
    Node node7 = new Node(7);
    Node node8 = new Node(8);

    node4.left = node2;
    node4.right = node6;
    node2.left = node1;
//  node2.right = node3;
    node6.left = node5;
    node6.right = node7;
    node1.left = node8;
    obj.printATreeLevelByLevel(node4);
}

}


1

答案已经接近了……我唯一能看到的问题是,如果树在特定位置没有节点,你会将该指针设置为 null。当你尝试将一个空指针放入列表中时会发生什么?

这是我最近任务中完成的一些内容。它完美无缺地工作。你可以从任何根开始使用它。

  //Prints the tree in level order
  public void printTree(){
    printTree(root);
  }

 public void printTree(TreeNode tmpRoot){

    //If the first node isn't null....continue on
    if(tmpRoot != null){

        Queue<TreeNode> currentLevel = new LinkedList<TreeNode>(); //Queue that holds the nodes on the current level
        Queue<TreeNode> nextLevel = new LinkedList<TreeNode>();     //Queue the stores the nodes for the next level

        int treeHeight = height(tmpRoot);     //Stores the height of the current tree
        int levelTotal = 0;  //keeps track of the total levels printed so we don't  pass the height and print a billion "null"s

        //put the root on the currnt level's queue
        currentLevel.add(tmpRoot);

        //while there is still another level to print and we haven't gone past the tree's height
        while(!currentLevel.isEmpty()&& (levelTotal< treeHeight)){

            //Print the next node on the level, add its childen to the next level's queue, and dequeue the node...do this until the current level has been printed
            while(!currentLevel.isEmpty()){

                //Print the current value
                System.out.print(currentLevel.peek().getValue()+" ");

                //If there is a left pointer, put the node on the nextLevel's stack. If there is no ponter, add a node with a null value to the next level's stack
                tmpRoot = currentLevel.peek().getLeft();
                if(tmpRoot != null)
                    nextLevel.add(tmpRoot);
                else
                    nextLevel.add(new TreeNode(null));

                //If there is a right pointer, put the node on the nextLevel's stack. If there is no ponter, add a node with a null value to the next level's stack
                tmpRoot = currentLevel.remove().getRight();
                if(tmpRoot != null)
                    nextLevel.add(tmpRoot);
                else
                    nextLevel.add(new TreeNode(null));

            }//end while(!currentLevel.isEmpty())

            //populate the currentLevel queue with items from the next level
            while(!nextLevel.isEmpty()){
                currentLevel.add(nextLevel.remove());
            }

            //Print a blank line to show height
            System.out.println("");

            //flag that we are working on the next level
            levelTotal++;

        }//end while(!currentLevel.isEmpty())

    }//end if(tmpRoot != null)

}//end method printTree

public int height(){
    return height(getRoot());
}

public int height(TreeNode tmpRoot){

    if (tmpRoot == null)
        return 0;
    int leftHeight = height(tmpRoot.getLeft());
    int rightHeight = height(tmpRoot.getRight());

    if(leftHeight >= rightHeight)
        return leftHeight + 1;
    else
        return rightHeight + 1;
 }

1
以下实现使用2个队列。这里使用了ListBlokcingQueue,但任何队列都可以使用。
import java.util.concurrent.*;

public class Test5 {

    public class Tree {
        private String value;
        private Tree left;
        private Tree right;

        public Tree(String value) {
            this.value = value;
        }

        public void setLeft(Tree t) {
            this.left = t;
        }

        public void setRight(Tree t) {
            this.right = t;
        }

        public Tree getLeft() {
            return this.left;
        }

        public Tree getRight() {
            return this.right;
        }

        public String getValue() {
            return this.value;
        }
    }

    Tree tree = null;

    public void setTree(Tree t) {
        this.tree = t;
    }

    public void printTree() {
        LinkedBlockingQueue<Tree> q = new LinkedBlockingQueue<Tree>();
        q.add(this.tree);
        while (true) {
            LinkedBlockingQueue<Tree> subQueue = new LinkedBlockingQueue<Tree>();
            while (!q.isEmpty()) {
                Tree aTree = q.remove();
                System.out.print(aTree.getValue() + ", ");
                if (aTree.getLeft() != null) {
                    subQueue.add(aTree.getLeft());
                }
                if (aTree.getRight() != null) {
                    subQueue.add(aTree.getRight());
                }
            }
            System.out.println("");
            if (subQueue.isEmpty()) {
                return;
            } else {
                q = subQueue;
            }
        }
    }

    public void testPrint() {
        Tree a = new Tree("A");
        a.setLeft(new Tree("B"));
        a.setRight(new Tree("C"));
        a.getLeft().setLeft(new Tree("D"));
        a.getLeft().setRight(new Tree("E"));
        a.getRight().setLeft(new Tree("F"));
        a.getRight().setRight(new Tree("G"));
        setTree(a);
        printTree();
    }

    public static void main(String args[]) {
        Test5 test5 = new Test5();
        test5.testPrint();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接