Làm thế nào để in sơ đồ cây nhị phân?


163

Làm cách nào tôi có thể in cây nhị phân trong Java để đầu ra giống như:

   4 
  / \ 
 2   5 

Nút của tôi:

public class Node<A extends Comparable> {
    Node<A> left, right;
    A data;

    public Node(A data){
        this.data = data;
    }
}

5
Đó là một điều khó khăn. Tôi nghĩ bạn phải xác định độ sâu của cây trước. Cá nhân, tôi chỉ cần đổ đồ thị nút vào graphviz và để nó xử lý nó. :-)
Omnifarious

Có vẻ như nếu bạn có nhiều yếu tố, yếu tố gốc sẽ có lợi thế lớn đến từ nó.

Tôi có một phương thức getDept () trong cây
Tian

1
Chỉ vì ý tưởng khiến tôi thích thú, tôi đã viết mã bằng C ++ và nó đã nhổ ra định dạng đồ thị graphviz. Cây định dạng đẹp.
Omnifarious

Câu trả lời:


238

Tôi đã tạo máy in cây nhị phân đơn giản. Bạn có thể sử dụng và sửa đổi nó theo ý muốn, nhưng dù sao nó cũng không được tối ưu hóa. Tôi nghĩ rằng rất nhiều thứ có thể được cải thiện ở đây;)

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BTreePrinterTest {

    private static Node<Integer> test1() {
        Node<Integer> root = new Node<Integer>(2);
        Node<Integer> n11 = new Node<Integer>(7);
        Node<Integer> n12 = new Node<Integer>(5);
        Node<Integer> n21 = new Node<Integer>(2);
        Node<Integer> n22 = new Node<Integer>(6);
        Node<Integer> n23 = new Node<Integer>(3);
        Node<Integer> n24 = new Node<Integer>(6);
        Node<Integer> n31 = new Node<Integer>(5);
        Node<Integer> n32 = new Node<Integer>(8);
        Node<Integer> n33 = new Node<Integer>(4);
        Node<Integer> n34 = new Node<Integer>(5);
        Node<Integer> n35 = new Node<Integer>(8);
        Node<Integer> n36 = new Node<Integer>(4);
        Node<Integer> n37 = new Node<Integer>(5);
        Node<Integer> n38 = new Node<Integer>(8);

        root.left = n11;
        root.right = n12;

        n11.left = n21;
        n11.right = n22;
        n12.left = n23;
        n12.right = n24;

        n21.left = n31;
        n21.right = n32;
        n22.left = n33;
        n22.right = n34;
        n23.left = n35;
        n23.right = n36;
        n24.left = n37;
        n24.right = n38;

        return root;
    }

    private static Node<Integer> test2() {
        Node<Integer> root = new Node<Integer>(2);
        Node<Integer> n11 = new Node<Integer>(7);
        Node<Integer> n12 = new Node<Integer>(5);
        Node<Integer> n21 = new Node<Integer>(2);
        Node<Integer> n22 = new Node<Integer>(6);
        Node<Integer> n23 = new Node<Integer>(9);
        Node<Integer> n31 = new Node<Integer>(5);
        Node<Integer> n32 = new Node<Integer>(8);
        Node<Integer> n33 = new Node<Integer>(4);

        root.left = n11;
        root.right = n12;

        n11.left = n21;
        n11.right = n22;

        n12.right = n23;
        n22.left = n31;
        n22.right = n32;

        n23.left = n33;

        return root;
    }

    public static void main(String[] args) {

        BTreePrinter.printNode(test1());
        BTreePrinter.printNode(test2());

    }
}

class Node<T extends Comparable<?>> {
    Node<T> left, right;
    T data;

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

class BTreePrinter {

    public static <T extends Comparable<?>> void printNode(Node<T> root) {
        int maxLevel = BTreePrinter.maxLevel(root);

        printNodeInternal(Collections.singletonList(root), 1, maxLevel);
    }

    private static <T extends Comparable<?>> void printNodeInternal(List<Node<T>> nodes, int level, int maxLevel) {
        if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes))
            return;

        int floor = maxLevel - level;
        int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
        int firstSpaces = (int) Math.pow(2, (floor)) - 1;
        int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;

        BTreePrinter.printWhitespaces(firstSpaces);

        List<Node<T>> newNodes = new ArrayList<Node<T>>();
        for (Node<T> node : nodes) {
            if (node != null) {
                System.out.print(node.data);
                newNodes.add(node.left);
                newNodes.add(node.right);
            } else {
                newNodes.add(null);
                newNodes.add(null);
                System.out.print(" ");
            }

            BTreePrinter.printWhitespaces(betweenSpaces);
        }
        System.out.println("");

        for (int i = 1; i <= endgeLines; i++) {
            for (int j = 0; j < nodes.size(); j++) {
                BTreePrinter.printWhitespaces(firstSpaces - i);
                if (nodes.get(j) == null) {
                    BTreePrinter.printWhitespaces(endgeLines + endgeLines + i + 1);
                    continue;
                }

                if (nodes.get(j).left != null)
                    System.out.print("/");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(i + i - 1);

                if (nodes.get(j).right != null)
                    System.out.print("\\");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(endgeLines + endgeLines - i);
            }

            System.out.println("");
        }

        printNodeInternal(newNodes, level + 1, maxLevel);
    }

    private static void printWhitespaces(int count) {
        for (int i = 0; i < count; i++)
            System.out.print(" ");
    }

    private static <T extends Comparable<?>> int maxLevel(Node<T> node) {
        if (node == null)
            return 0;

        return Math.max(BTreePrinter.maxLevel(node.left), BTreePrinter.maxLevel(node.right)) + 1;
    }

    private static <T> boolean isAllElementsNull(List<T> list) {
        for (Object object : list) {
            if (object != null)
                return false;
        }

        return true;
    }

}

Đầu ra 1:

         2               
        / \       
       /   \      
      /     \     
     /       \    
     7       5       
    / \     / \   
   /   \   /   \  
   2   6   3   6   
  / \ / \ / \ / \ 
  5 8 4 5 8 4 5 8 

Đầu ra 2:

       2               
      / \       
     /   \      
    /     \     
   /       \    
   7       5       
  / \       \   
 /   \       \  
 2   6       9   
    / \     /   
    5 8     4   

1
Làm thế nào để chuyển đổi đầu ra này sang ngang?
jijesh Aj

Đối với đầu ra ngang tốt hơn là sử dụng giải pháp của Vasya Novikov.
michal.kreuzman

3
Sẽ thật tuyệt nếu bạn có thể giải thích về việc chọn 2 ^ n - 1 làm khoảng trắng đầu tiên và 2 ^ (n + 1) - 1 làm khoảng cách giữa các không gian
DJ '

Nó là tốt cho cây cân bằng khi tôi đã thử nó cho một trong những cây bị lệch phải của 15 giá trị và nó trở nên rất khó kiểm soát để xem bản in.
akhil_mittal

3
Cây của tôi sâu 44 lớp, vì vậy java gặp sự cố khi in 8796093022207 khoảng trắng. Vì vậy, được cảnh báo.
Game thủ CX

286

In một cây [lớn] theo dòng.

ví dụ đầu ra:

z
├── c
   ├── a
   └── b
├── d
├── e
   └── asdf
└── f

mã:

public class TreeNode {

    final String name;
    final List<TreeNode> children;

    public TreeNode(String name, List<TreeNode> children) {
        this.name = name;
        this.children = children;
    }

    public String toString() {
        StringBuilder buffer = new StringBuilder(50);
        print(buffer, "", "");
        return buffer.toString();
    }

    private void print(StringBuilder buffer, String prefix, String childrenPrefix) {
        buffer.append(prefix);
        buffer.append(name);
        buffer.append('\n');
        for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) {
            TreeNode next = it.next();
            if (it.hasNext()) {
                next.print(buffer, childrenPrefix + "├── ", childrenPrefix + "│   ");
            } else {
                next.print(buffer, childrenPrefix + "└── ", childrenPrefix + "    ");
            }
        }
    }
}

PS Câu trả lời này không tập trung chính xác vào cây "nhị phân" - thay vào đó, nó in tất cả các loại cây. Giải pháp được lấy cảm hứng từ lệnh "cây" trong linux.


Liệu giải pháp này xử lý cây nhị phân lệch phải?
sáng chế

@VasyaNovikov bạn sẽ viết lại như thế nào children.get(children.size() - 1)nếu HashMap được sử dụng cho trẻ em? Tôi quản lý để sửa đổi tất cả các phần khác nhưng phần này.
Lê Nguyễn Duy Anh

@LeNguyenDuyAnh chữ ký loại đề xuất HashMap là gì? HashMap<String, List<String>>?
VasiliNovikov

Tôi đã thực hiện cây của tôi như HashMap<String, Node>. Chuỗi là id của Node.
Lê Nguyễn Duy Anh

Tôi thực sự đã triển khai một cái gì đó tương tự trong một thư viện Java nhỏ gọi là text-tree . Có lẽ nó giúp được ai đó.
barfuin

47

Tôi đã thực hiện một thuật toán cải tiến cho việc này, xử lý các nút độc đáo với kích thước khác nhau. Nó in từ trên xuống bằng cách sử dụng các dòng.

package alg;

import java.util.ArrayList;
import java.util.List;


/**
 * Binary tree printer
 * 
 * @author MightyPork
 */
public class TreePrinter
{
    /** Node that can be printed */
    public interface PrintableNode
    {
        /** Get left child */
        PrintableNode getLeft();


        /** Get right child */
        PrintableNode getRight();


        /** Get text to be printed */
        String getText();
    }


    /**
     * Print a tree
     * 
     * @param root
     *            tree root node
     */
    public static void print(PrintableNode root)
    {
        List<List<String>> lines = new ArrayList<List<String>>();

        List<PrintableNode> level = new ArrayList<PrintableNode>();
        List<PrintableNode> next = new ArrayList<PrintableNode>();

        level.add(root);
        int nn = 1;

        int widest = 0;

        while (nn != 0) {
            List<String> line = new ArrayList<String>();

            nn = 0;

            for (PrintableNode n : level) {
                if (n == null) {
                    line.add(null);

                    next.add(null);
                    next.add(null);
                } else {
                    String aa = n.getText();
                    line.add(aa);
                    if (aa.length() > widest) widest = aa.length();

                    next.add(n.getLeft());
                    next.add(n.getRight());

                    if (n.getLeft() != null) nn++;
                    if (n.getRight() != null) nn++;
                }
            }

            if (widest % 2 == 1) widest++;

            lines.add(line);

            List<PrintableNode> tmp = level;
            level = next;
            next = tmp;
            next.clear();
        }

        int perpiece = lines.get(lines.size() - 1).size() * (widest + 4);
        for (int i = 0; i < lines.size(); i++) {
            List<String> line = lines.get(i);
            int hpw = (int) Math.floor(perpiece / 2f) - 1;

            if (i > 0) {
                for (int j = 0; j < line.size(); j++) {

                    // split node
                    char c = ' ';
                    if (j % 2 == 1) {
                        if (line.get(j - 1) != null) {
                            c = (line.get(j) != null) ? '┴' : '┘';
                        } else {
                            if (j < line.size() && line.get(j) != null) c = '└';
                        }
                    }
                    System.out.print(c);

                    // lines and spaces
                    if (line.get(j) == null) {
                        for (int k = 0; k < perpiece - 1; k++) {
                            System.out.print(" ");
                        }
                    } else {

                        for (int k = 0; k < hpw; k++) {
                            System.out.print(j % 2 == 0 ? " " : "─");
                        }
                        System.out.print(j % 2 == 0 ? "┌" : "┐");
                        for (int k = 0; k < hpw; k++) {
                            System.out.print(j % 2 == 0 ? "─" : " ");
                        }
                    }
                }
                System.out.println();
            }

            // print line of numbers
            for (int j = 0; j < line.size(); j++) {

                String f = line.get(j);
                if (f == null) f = "";
                int gap1 = (int) Math.ceil(perpiece / 2f - f.length() / 2f);
                int gap2 = (int) Math.floor(perpiece / 2f - f.length() / 2f);

                // a number
                for (int k = 0; k < gap1; k++) {
                    System.out.print(" ");
                }
                System.out.print(f);
                for (int k = 0; k < gap2; k++) {
                    System.out.print(" ");
                }
            }
            System.out.println();

            perpiece /= 2;
        }
    }
}

Để sử dụng điều này cho Cây của bạn, hãy để Nodelớp của bạn thực hiện PrintableNode.

Ví dụ đầu ra:

                                         2952:0                                             
                    ┌───────────────────────┴───────────────────────┐                       
                 1249:-1                                         5866:0                     
        ┌───────────┴───────────┐                       ┌───────────┴───────────┐           
     491:-1                  1572:0                  4786:1                  6190:0         
  ┌─────┘                                               └─────┐           ┌─────┴─────┐     
339:0                                                      5717:0      6061:0      6271:0   

Tôi đã cố gắng sao chép kỹ thuật "câu trả lời được chọn". Nhưng tôi nghĩ đây là một trong những câu trả lời tốt nhất ở đây. Thật mạnh mẽ và súc tích.
Vikrant Goel

Sau khi thực hiện điều này, nó có vẻ hoạt động tuyệt vời, nhưng chỉ đối với cây cân bằng. Bất cứ điều gì mất cân bằng trả về kết quả kỳ lạ.
mitbanip

Tôi nhận được ???????????thay vì các dòng giữa các nút nhưng chỉ là một số vấn đề về UTF8 ans. Dù sao, công cụ tuyệt vời, tôi phải nói. Câu trả lời tốt nhất cho tôi vì nó thực sự dễ sử dụng.
Fitz

Vâng, chính là nó. Chỉ cần thay đổi tất cả các ký tự đặc biệt của dòng và khoảng trắng đoạn của bạn.
Fitz

Thật tuyệt, để hỗ trợ in một loạt các yếu tố, đã tạo ra một ý chính chỉ sử dụng logic @MightyPork để in cây. Xempublic static <T> void print(T[] elems)
Neo

40
public static class Node<T extends Comparable<T>> {
    T value;
    Node<T> left, right;

    public void insertToTree(T v) {
        if (value == null) {
            value = v;
            return;
        }
        if (v.compareTo(value) < 0) {
            if (left == null) {
                left = new Node<T>();
            }
            left.insertToTree(v);
        } else {
            if (right == null) {
                right = new Node<T>();
            }
            right.insertToTree(v);
        }
    }

    public void printTree(OutputStreamWriter out) throws IOException {
        if (right != null) {
            right.printTree(out, true, "");
        }
        printNodeValue(out);
        if (left != null) {
            left.printTree(out, false, "");
        }
    }
    private void printNodeValue(OutputStreamWriter out) throws IOException {
        if (value == null) {
            out.write("<null>");
        } else {
            out.write(value.toString());
        }
        out.write('\n');
    }
    // use string and not stringbuffer on purpose as we need to change the indent at each recursion
    private void printTree(OutputStreamWriter out, boolean isRight, String indent) throws IOException {
        if (right != null) {
            right.printTree(out, true, indent + (isRight ? "        " : " |      "));
        }
        out.write(indent);
        if (isRight) {
            out.write(" /");
        } else {
            out.write(" \\");
        }
        out.write("----- ");
        printNodeValue(out);
        if (left != null) {
            left.printTree(out, false, indent + (isRight ? " |      " : "        "));
        }
    }

}

sẽ in:

                 /----- 20
                 |       \----- 15
         /----- 14
         |       \----- 13
 /----- 12
 |       |       /----- 11
 |       \----- 10
 |               \----- 9
8
 |               /----- 7
 |       /----- 6
 |       |       \----- 5
 \----- 4
         |       /----- 3
         \----- 2
                 \----- 1

cho đầu vào

8 4 12 2 6 10 14 1 3 5 7 9 11 13 20 15

đây là một biến thể từ câu trả lời của @ anurag - nó đã làm tôi khó chịu khi thấy thêm


Thật tuyệt vời nếu bạn có thể xoay nó 90 °.
Abhijit Sarkar

34

Chuyển thể từ câu trả lời của Vasya Novikov để biến nó thành nhị phân hơn và sử dụng hiệu quả ( các đối tượng ghép lại với nhau trong Java nói chung là không hiệu quả).StringBuilderString

public StringBuilder toString(StringBuilder prefix, boolean isTail, StringBuilder sb) {
    if(right!=null) {
        right.toString(new StringBuilder().append(prefix).append(isTail ? "│   " : "    "), false, sb);
    }
    sb.append(prefix).append(isTail ? "└── " : "┌── ").append(value.toString()).append("\n");
    if(left!=null) {
        left.toString(new StringBuilder().append(prefix).append(isTail ? "    " : "│   "), true, sb);
    }
    return sb;
}

@Override
public String toString() {
    return this.toString(new StringBuilder(), true, new StringBuilder()).toString();
}

Đầu ra:

       ┌── 7
   ┌── 6
      └── 5
└── 4
       ┌── 3
    └── 2
        └── 1
            └── 0

Nó không hoạt động đối với cây khi chúng ta chèn các giá trị: 30,40,50,60,70,80 vào BST. Như điều đó tạo ra một cây xiên phải. Giá trị của isTail sẽ là sai khi. right != nullTôi đã chỉnh sửa và kiểm tra nó, nó hoạt động tốt.
akhil_mittal

Cảm ơn cho đầu vào, tôi chỉ cần chỉnh sửa câu trả lời, nó tốt hơn?
Todd Davies

Cảm ơn bạn, câu trả lời của @Vasya Novikov rất hay nhưng tôi cần một phiên bản danh sách liên kết của nó, và câu trả lời của bạn phù hợp với trường hợp của tôi.
hychou

Trong tất cả các câu trả lời, điều này tạo ra cây trông đẹp nhất và mã rất sạch sẽ!
p-sun

15

michal.kreuzman tốt đẹp tôi sẽ phải nói.

Tôi đã cảm thấy lười biếng khi tự làm một chương trình và tìm kiếm mã trên mạng khi tôi thấy nó thực sự giúp ích cho tôi.

Nhưng tôi e ngại khi thấy rằng nó chỉ hoạt động với một chữ số như thể bạn sẽ sử dụng nhiều hơn một chữ số, vì bạn đang sử dụng khoảng trắng và không phải các tab, cấu trúc sẽ bị đặt sai vị trí và chương trình sẽ mất sử dụng.

Đối với các mã sau này của tôi, tôi cần một số đầu vào lớn hơn (ít nhất là hơn 10), điều này không hiệu quả với tôi và sau khi tìm kiếm rất nhiều trên mạng khi tôi không tìm thấy gì, tôi đã tự mình tạo một chương trình.

Bây giờ nó có một số lỗi, một lần nữa tôi cảm thấy lười sửa chúng nhưng nó in rất đẹp và các nút có thể nhận bất kỳ giá trị lớn nào.

Cây sẽ không như câu hỏi đề cập nhưng nó được xoay 270 độ :)

public static void printBinaryTree(TreeNode root, int level){
    if(root==null)
         return;
    printBinaryTree(root.right, level+1);
    if(level!=0){
        for(int i=0;i<level-1;i++)
            System.out.print("|\t");
            System.out.println("|-------"+root.val);
    }
    else
        System.out.println(root.val);
    printBinaryTree(root.left, level+1);
}    

Đặt chức năng này với TreeNode được chỉ định của riêng bạn và giữ mức ban đầu là 0 và tận hưởng!

Dưới đây là một số kết quả đầu ra mẫu:

|       |       |-------11
|       |-------10
|       |       |-------9
|-------8
|       |       |-------7
|       |-------6
|       |       |-------5
4
|       |-------3
|-------2
|       |-------1


|       |       |       |-------10
|       |       |-------9
|       |-------8
|       |       |-------7
|-------6
|       |-------5
4
|       |-------3
|-------2
|       |-------1

Chỉ có vấn đề là với các chi nhánh mở rộng; Tôi sẽ cố gắng giải quyết vấn đề càng sớm càng tốt nhưng đến lúc đó bạn cũng có thể sử dụng nó.


14

Cây của bạn sẽ cần gấp đôi khoảng cách cho mỗi lớp:

       một
      / \
     / \
    / \
   / \
   bc
  / \ / \
 / \ / \
 defg
/ \ / \ / \ / \
khăn trùm đầu

Bạn có thể lưu cây của bạn trong một mảng các mảng, một mảng cho mỗi độ sâu:

[[a], [b, c], [d, e, f, g], [h, i, j, k, l, m, n, o]]

Nếu cây của bạn không đầy, bạn cần bao gồm các giá trị trống trong mảng đó:

       một
      / \
     / \
    / \
   / \
   bc
  / \ / \
 / \ / \
 defg
/ \ \ / \ \
hiklmo
[[a], [b, c], [d, e, f, g], [h, i ,, k, l, m ,, o]]

Sau đó, bạn có thể lặp qua mảng để in cây của mình, in khoảng trắng trước phần tử đầu tiên và giữa các phần tử tùy thuộc vào độ sâu và in các dòng tùy thuộc vào việc các phần tử tương ứng trong mảng cho lớp tiếp theo có được lấp đầy hay không. Nếu các giá trị của bạn có thể dài hơn một ký tự, bạn cần tìm giá trị dài nhất trong khi tạo biểu diễn mảng và nhân tất cả các chiều rộng và số lượng dòng tương ứng.


Nếu cây không hoàn thành thì sao? Trong trường hợp đó, có vẻ như bạn sẽ có thể làm điều này mà không cần nhân đôi không gian ở mỗi cấp.
templatetypedef

Có, nhưng chỉ trong một số trường hợp rất hạn chế trong đó hầu hết các cây con được liệt kê liên kết thay vì cây từ cùng cấp trở xuống hoặc bạn sẽ vẽ các cây con khác nhau với khoảng cách khác nhau giữa các lớp ...
hd42

13

Tôi thấy câu trả lời của VasyaNovikov rất hữu ích cho việc in một cây tổng quát lớn và sửa đổi nó thành cây nhị phân

Mã số:

class TreeNode {
    Integer data = null;
    TreeNode left = null;
    TreeNode right = null;

    TreeNode(Integer data) {this.data = data;}

    public void print() {
        print("", this, false);
    }

    public void print(String prefix, TreeNode n, boolean isLeft) {
        if (n != null) {
            System.out.println (prefix + (isLeft ? "|-- " : "\\-- ") + n.data);
            print(prefix + (isLeft ? "|   " : "    "), n.left, true);
            print(prefix + (isLeft ? "|   " : "    "), n.right, false);
        }
    }
}

Đầu ra mẫu:

\-- 7
    |-- 3
    |   |-- 1
    |   |   \-- 2
    |   \-- 5
    |       |-- 4
    |       \-- 6
    \-- 11
        |-- 9
        |   |-- 8
        |   \-- 10
        \-- 13
            |-- 12
            \-- 14

8

Một giải pháp trong ngôn ngữ Scala , tương tự như những gì tôi đã viết trong java :

case class Node(name: String, children: Node*) {

    def toTree: String = toTree("", "").mkString("\n")

    private def toTree(prefix: String, childrenPrefix: String): Seq[String] = {
        val firstLine = prefix + this.name

        val firstChildren = this.children.dropRight(1).flatMap { child =>
            child.toTree(childrenPrefix + "├── ", childrenPrefix + "│   ")
        }
        val lastChild = this.children.takeRight(1).flatMap { child =>
            child.toTree(childrenPrefix + "└── ", childrenPrefix + "    ")
        }
        firstLine +: firstChildren ++: lastChild
    }

}

Ví dụ đầu ra:

vasya
├── frosya
   ├── petya
      └── masha
   └── kolya
└── frosya2

1
Với Lambda cũng có sẵn trong Java, có lẽ bạn muốn cập nhật giải pháp Java của mình?
Tintin

@Tintin Scala hoàn toàn không chỉ là về các chức năng lambda. Nhưng nếu bạn có một cải tiến tốt cho Java, hãy thoải mái "chỉnh sửa", sau đó sẽ được cộng đồng StackOverflow chấp nhận nếu được coi là có lợi.;)
VasiliNovikov

5

Tôi biết tất cả các bạn đều có giải pháp tuyệt vời; Tôi chỉ muốn chia sẻ của tôi - có thể đó không phải là cách tốt nhất, nhưng nó là hoàn hảo cho bản thân tôi!

Với pythonpiptrên, nó thực sự khá đơn giản! BÙM!

Trên Mac hoặc Ubuntu (của tôi là mac)

  1. mở thiết bị đầu cuối
  2. $ pip install drawtree
  3. $python, nhập bảng điều khiển python; bạn có thể làm theo cách khác
  4. from drawtree import draw_level_order
  5. draw_level_order('{2,1,3,0,7,9,1,2,#,1,0,#,#,8,8,#,#,#,#,7}')

LÀM XONG!

        2
       / \
      /   \
     /     \
    1       3
   / \     / \
  0   7   9   1
 /   / \     / \
2   1   0   8   8
       /
      7

Theo dõi nguồn:

Trước khi tôi thấy bài đăng này, tôi đã đi google "văn bản thuần cây nhị phân"

Và tôi đã tìm thấy https://www.reddit.com/r/learnpython/comments/3naiq8/draw_binary_tree_in_plain tựa / , hướng tôi đến https://github.com/msbanik/drawtree này


@DenysVitali ồ, đúng vậy
Sean L

3
Tôi không muốn tỏ ra thô lỗ, nhưng khi một người dùng gắn thẻ câu hỏi vì javaanh ta mong đợi câu trả lời Java :)
Denys Vitali

3
public void printPreety() {
    List<TreeNode> list = new ArrayList<TreeNode>();
    list.add(head);
    printTree(list, getHeight(head));
}

public int getHeight(TreeNode head) {

    if (head == null) {
        return 0;
    } else {
        return 1 + Math.max(getHeight(head.left), getHeight(head.right));
    }
}

/**
 * pass head node in list and height of the tree 
 * 
 * @param levelNodes
 * @param level
 */
private void printTree(List<TreeNode> levelNodes, int level) {

    List<TreeNode> nodes = new ArrayList<TreeNode>();

    //indentation for first node in given level
    printIndentForLevel(level);

    for (TreeNode treeNode : levelNodes) {

        //print node data
        System.out.print(treeNode == null?" ":treeNode.data);

        //spacing between nodes
        printSpacingBetweenNodes(level);

        //if its not a leaf node
        if(level>1){
            nodes.add(treeNode == null? null:treeNode.left);
            nodes.add(treeNode == null? null:treeNode.right);
        }
    }
    System.out.println();

    if(level>1){        
        printTree(nodes, level-1);
    }
}

private void printIndentForLevel(int level){
    for (int i = (int) (Math.pow(2,level-1)); i >0; i--) {
        System.out.print(" ");
    }
}

private void printSpacingBetweenNodes(int level){
    //spacing between nodes
    for (int i = (int) ((Math.pow(2,level-1))*2)-1; i >0; i--) {
        System.out.print(" ");
    }
}


Prints Tree in following format:
                4                               
        3               7               
    1               5       8       
      2                       10   
                             9   

2

Đây là một giải pháp rất đơn giản để in ra một cái cây. Nó không phải là đẹp, nhưng nó thực sự đơn giản:

enum { kWidth = 6 };
void PrintSpace(int n)
{
  for (int i = 0; i < n; ++i)
    printf(" ");
}

void PrintTree(struct Node * root, int level)
{
  if (!root) return;
  PrintTree(root->right, level + 1);
  PrintSpace(level * kWidth);
  printf("%d", root->data);
  PrintTree(root->left, level + 1);
}

Đầu ra mẫu:

      106
            105
104
            103
                  102
                        101
      100

2

Dựa trên câu trả lời của VasyaNovikov. Được cải tiến với một số phép thuật Java: Giao diện Generics và Functional.

/**
 * Print a tree structure in a pretty ASCII fromat.
 * @param prefix Currnet previx. Use "" in initial call!
 * @param node The current node. Pass the root node of your tree in initial call.
 * @param getChildrenFunc A {@link Function} that returns the children of a given node.
 * @param isTail Is node the last of its sibblings. Use true in initial call. (This is needed for pretty printing.)
 * @param <T> The type of your nodes. Anything that has a toString can be used.
 */
private <T> void printTreeRec(String prefix, T node, Function<T, List<T>> getChildrenFunc, boolean isTail) {
    String nodeName = node.toString();
    String nodeConnection = isTail ? "└── " : "├── ";
    log.debug(prefix + nodeConnection + nodeName);
    List<T> children = getChildrenFunc.apply(node);
    for (int i = 0; i < children.size(); i++) {
        String newPrefix = prefix + (isTail ? "    " : "│   ");
        printTreeRec(newPrefix, children.get(i), getChildrenFunc, i == children.size()-1);
    }
}

Ví dụ cuộc gọi ban đầu:

Function<ChecksumModel, List<ChecksumModel>> getChildrenFunc = node -> getChildrenOf(node)
printTreeRec("", rootNode, getChildrenFunc, true);

Sẽ xuất ra một cái gì đó như

└── rootNode
    ├── childNode1
    ├── childNode2
       ├── childNode2.1
       ├── childNode2.2
       └── childNode2.3
    ├── childNode3
    └── childNode4

2

Tôi đã viết một máy in cây nhị phân trong Java.

trên GitHub ở đây .

Nó không được tối ưu hóa cho hiệu quả thời gian chạy, nhưng vì chúng ta đang nói về việc in bằng ASCII, tôi đoán rằng nó sẽ không được sử dụng trên các cây rất lớn. Nó có một số tính năng tốt mặc dù.

  1. Nó sử dụng hiệu quả không gian trong đó một cây con lớn kéo dài dưới một cái nhỏ nhất có thể.
  2. Có một tham số để đặt không gian ngang tối thiểu giữa các nhãn nút.
  3. Nhãn nút là các chuỗi có độ dài tùy ý.
  4. Ngoài phương pháp in một cây, còn có một phương pháp in danh sách các cây theo chiều ngang trên trang (với tham số cho chiều rộng trang), sử dụng càng nhiều hàng càng cần thiết.
  5. Có một tùy chọn để in cây với các nhánh chéo (sử dụng các ký tự gạch chéo và dấu gạch chéo ngược) hoặc với các nhánh ngang (sử dụng các ký tự vẽ hộp ascii). Loại thứ hai nhỏ gọn hơn và làm cho mức độ cây rõ ràng hơn.
  6. Nó hoạt động.

Một số chương trình demo / thử nghiệm được bao gồm.

Một ví dụ về cây nhị phân được tạo ngẫu nhiên, như được in bởi chương trình, sau đây. Điều này minh họa việc sử dụng không gian hiệu quả, với một cây con lớn bên phải kéo dài dưới một cây con nhỏ bên trái:

             seven                                        
              / \                                         
             /   \                                        
            /     \                                       
           /       \                                      
          /         \                                     
         /           \                                    
       five        thirteen                               
       / \           / \                                  
      /   \         /   \                                 
     /     \       /     \                                
  three    six    /       \                               
   / \           /         \                              
  /   \         /           \                             
one   four     /             \                            
  \           /               \                           
  two        /                 \                          
           nine            twenty four                    
           / \                 / \                        
          /   \               /   \                       
         /     \             /     \                      
      eight   twelve        /       \                     
               /           /         \                    
             ten          /           \                   
               \         /             \                  
              eleven    /               \                 
                       /                 \                
                      /                   \               
                     /                     \              
                 eighteen              twenty seven       
                   / \                     / \            
                  /   \                   /   \           
                 /     \                 /     \          
                /       \               /       \         
               /         \             /         \        
              /           \           /           \       
             /             \    twenty five   twenty eight
            /               \         \             \     
           /                 \     twenty six      thirty 
       fourteen            nineteen                 /     
           \                   \              twenty nine 
         sixteen           twenty three                   
           / \                 /                          
          /   \           twenty two                      
         /     \             /                            
        /       \         twenty                          
       /         \           \                            
   fifteen    seventeen   twenty one                      

Một ví dụ về in tất cả năm cây nhị phân nút (có nhãn theo thứ tự) trên toàn trang:

one           one         one          one        one       one         one     
  \             \           \            \          \         \           \     
  two           two         two          two        two      three       three  
    \             \           \            \          \       / \         / \   
   three         three        four         five       five  two four    two five
      \             \         / \          /          /           \         /   
      four          five     /   \      three       four          five    four  
        \           /     three  five      \        /                           
        five      four                     four  three                          



one          one        one        one       one       one         one        two        
  \            \          \          \         \         \           \        / \        
  four         four       five       five      five      five        five    /   \       
  / \          / \        /          /         /         /           /     one  three    
two five      /   \     two        two      three      four        four            \     
  \        three  five    \          \       / \       /           /               four  
 three      /            three       four  two four  two        three                \   
          two               \        /                 \         /                   five
                            four  three               three    two                       



   two          two          two        two      three         three         three    
   / \          / \          / \        / \       / \           / \           / \     
  /   \       one four     one five   one five  one four       /   \        two four  
one  three        / \          /          /       \   \       /     \       /     \   
        \        /   \      three       four      two five  one     five  one     five
        five  three  five      \        /                     \     /                 
        /                      four  three                    two four                
      four                                                                            



   three      four      four         four         four            four       five    
    / \       / \       / \          / \          / \             / \        /       
  two five  one five  one five     two five      /   \           /   \     one       
  /   /       \         \          / \        three  five     three  five    \       
one four      two      three      /   \        /               /             two     
                \       /       one  three   one             two               \     
               three  two                      \             /                three  
                                               two         one                   \   
                                                                                 four



  five      five      five      five       five         five      five        five
  /         /         /         /          /            /         /           /   
one       one       one       one        two          two      three       three  
  \         \         \         \        / \          / \       / \         / \   
  two      three      four      four    /   \       one four  one four    two four
    \       / \       /         /     one  three        /       \         /       
    four  two four  two      three            \      three      two     one       
    /                 \       /               four                                
 three               three  two                                                   



    five      five         five        five          five
    /         /            /           /             /   
  four      four         four        four          four  
  /         /            /           /             /     
one       one          two        three         three    
  \         \          / \         /             /       
  two      three      /   \      one           two       
    \       /       one  three     \           /         
   three  two                      two       one 

Sau đây là một ví dụ về cùng một cây được in 4 cách khác nhau, với khoảng cách ngang là 1 và 3, và với các nhánh chéo và ngang.

                   27        
             ┌─────┴─────┐   
             13          29  
      ┌──────┴──────┐  ┌─┴─┐ 
      8             23 28  30
   ┌──┴──┐       ┌──┴──┐     
   4     11      21    26    
 ┌─┴─┐  ┌┴┐    ┌─┴─┐  ┌┘     
 2   5  9 12   18  22 24     
┌┴┐  └┐ └┐   ┌─┴─┐    └┐     
1 3   6  10  17  19    25    
      └┐    ┌┘   └┐          
       7    15    20         
          ┌─┴─┐              
          14  16             


                 27        
                / \        
               /   \       
              13    29     
             / \   / \     
            /   \ 28  30   
           /     \         
          /       \        
         /         \       
        /           \      
       8             23    
      / \           / \    
     /   \         /   \   
    4     11      /     \  
   / \   / \     21      26
  2   5 9   12  / \     /  
 / \   \ \     18  22  24  
1   3   6 10  / \       \  
         \   17  19      25
          7 /     \        
           15      20      
          / \              
         14  16            


                             27            
                    ┌────────┴────────┐    
                    13                29   
          ┌─────────┴─────────┐    ┌──┴──┐ 
          8                   23   28    30
     ┌────┴────┐         ┌────┴────┐       
     4         11        21        26      
  ┌──┴──┐    ┌─┴─┐    ┌──┴──┐     ┌┘       
  2     5    9   12   18    22    24       
┌─┴─┐   └┐   └┐    ┌──┴──┐        └┐       
1   3    6    10   17    19        25      
         └┐       ┌┘     └┐                
          7       15      20               
               ┌──┴──┐                     
               14    16                    


                      27         
                     / \         
                    /   \        
                   /     \       
                  /       \      
                 13        29    
                / \       / \    
               /   \     /   \   
              /     \   28    30 
             /       \           
            /         \          
           /           \         
          /             \        
         /               \       
        8                 23     
       / \               / \     
      /   \             /   \    
     /     \           /     \   
    4       11        /       \  
   / \     / \       21        26
  2   5   9   12    / \       /  
 / \   \   \       /   \     24  
1   3   6   10    18    22    \  
         \       / \           25
          7     /   \            
               17    19          
              /       \          
             15        20        
            / \                  
           /   \                 
          14    16               

Thật tuyệt khi bạn đã viết một dự án được xuất bản này. Có vẻ như đó là một công việc tốt, nhưng về cơ bản chỉ là một liên kết đến thư viện của bạn không tạo ra câu trả lời hay trên Stack Overflow. Tối thiểu, bạn nên bao gồm mã cần thiết để sử dụng thư viện của mình để hiển thị các ví dụ bạn đã cung cấp, để mọi người biết những gì liên quan đến việc sử dụng thư viện của bạn. Ngay bây giờ, đây chỉ là một quảng cáo cho repo GitHub của bạn. Đó không phải là một điều xấu, nếu bạn chỉ cho mọi người cách sử dụng nó.
Makyen

BTW: Nếu bạn chỉnh sửa mã ví dụ, vui lòng ping tôi ở đây bằng cách đưa @Makyenvào một nhận xét.
Makyen

2

Đây là một câu hỏi thú vị, và tôi cũng đã viết một dự án cho nó.

máy in cây nhị phân

Dưới đây là một số ví dụ:

In BST ngẫu nhiên.

BTPrinter.printRandomBST(100, 100);
                              38                                  
                              / \                                 
                             /   \                                
                            /     \                               
                           /       \                              
                          /         \                             
                         /           \                            
                        /             \                           
                       /               \                          
                      /                 \                         
                     /                   \                        
                    /                     \                       
                   /                       \                      
                  /                         \                     
                 /                           \                    
                /                             \                   
               /                               \                  
              28                               82                 
             / \                               / \                
            /   \                             /   \               
           /     \                           /     \              
          /       \                         /       \             
         5        31                       /         \            
        / \       / \                     /           \           
       /   \     30 36                   /             \          
      /     \   /   / \                 /               \         
     /       \ 29  33 37               /                 \        
    /         \   / \                 /                   \       
   /           \ 32 35               65                   95      
  1            14   /               / \                   / \     
 / \           / \ 34              /   \                 94 97    
0   2         /   \               /     \               /   / \   
     \       12   24             /       \             93  96 98  
      3     / \   / \           /         \           /         \ 
       \   9  13 16 25         /           \         84         99
        4 / \   / \   \       /             \       / \           
         7  10 15 23  26     59             74     83 86          
        / \   \   /     \   / \             / \       / \         
       6   8  11 22     27 56 60           73 76     85 91        
                /         / \   \         /   / \       / \       
               20        /   \  61       67  75 79     88 92      
              / \       40   58   \     / \     / \   / \         
             18 21     / \   /    62   66 72   78 80 87 89        
            / \       39 54 57      \     /   /     \     \       
           17 19         / \        64   69  77     81    90      
                        50 55       /   / \                       
                       / \         63  68 70                      
                      /   \                 \                     
                     /     \                71                    
                    47     53                                     
                   / \     /                                      
                  /   \   52                                      
                 42   49 /                                        
                / \   / 51                                        
               41 43 48                                           
                    \                                             
                    46                                            
                    /                                             
                   45                                             
                  /                                               
                 44     

Cây in từ mảng thứ tự mức kiểu leetcode, '#' có nghĩa là dấu kết thúc đường dẫn trong đó không có nút nào tồn tại bên dưới.

BTPrinter.printTree("1,2,3,4,5,#,#,6,7,8,1,#,#,#,#,#,#,2,3,4,5,6,7,8,9,10,11,12,13,14,15");
        1              
       / \             
      2   3            
     / \               
    /   \              
   4     5             
  / \   / \            
 6   7 8   1           
          / \          
         /   \         
        /     \        
       /       \       
      /         \      
     2           3     
    / \         / \    
   /   \       /   \   
  4     5     6     7  
 / \   / \   / \   / \ 
8   9 10 11 12 13 14 15

1

Tôi cần in một cây nhị phân trong một trong các dự án của mình, vì tôi đã chuẩn bị một lớp java TreePrinter, một trong những đầu ra mẫu là:

                [+]
               /   \
              /     \
             /       \
            /         \
           /           \
        [*]             \
       /   \             [-]
[speed]     [2]         /   \
                    [45]     [12]

Đây là mã cho lớp TreePrintercùng với lớp TextNode. Để in bất kỳ cây nào, bạn chỉ cần tạo một cây tương đương với TextNodelớp.


import java.util.ArrayList;

public class TreePrinter {

    public TreePrinter(){
    }

    public static String TreeString(TextNode root){
        ArrayList layers = new ArrayList();
        ArrayList bottom = new ArrayList();

        FillBottom(bottom, root);  DrawEdges(root);

        int height = GetHeight(root);
        for(int i = 0; i  s.length()) min = s.length();

            if(!n.isEdge) s += "[";
            s += n.text;
            if(!n.isEdge) s += "]";

            layers.set(n.depth, s);
        }

        StringBuilder sb = new StringBuilder();

        for(int i = 0; i  temp = new ArrayList();

            for(int i = 0; i  0) temp.get(i-1).left = x;
                temp.add(x);
            }

            temp.get(count-1).left = n.left;
            n.left.depth = temp.get(count-1).depth+1;
            n.left = temp.get(0);

            DrawEdges(temp.get(count-1).left);
        }
        if(n.right != null){
            int count = n.right.x - (n.x + n.text.length() + 2);
            ArrayList temp = new ArrayList();

            for(int i = 0; i  0) temp.get(i-1).right = x;
                temp.add(x);
            }

            temp.get(count-1).right = n.right;
            n.right.depth = temp.get(count-1).depth+1;
            n.right = temp.get(0);  

            DrawEdges(temp.get(count-1).right);
        }
    }

    private static void FillBottom(ArrayList bottom, TextNode n){
        if(n == null) return;

        FillBottom(bottom, n.left);

        if(!bottom.isEmpty()){            
            int i = bottom.size()-1;
            while(bottom.get(i).isEdge) i--;
            TextNode last = bottom.get(i);

            if(!n.isEdge) n.x = last.x + last.text.length() + 3;
        }
        bottom.add(n);
        FillBottom(bottom, n.right);
    }

    private static boolean isLeaf(TextNode n){
        return (n.left == null && n.right == null);
    }

    private static int GetHeight(TextNode n){
        if(n == null) return 0;

        int l = GetHeight(n.left);
        int r = GetHeight(n.right);

        return Math.max(l, r) + 1;
    }
}


class TextNode {
    public String text;
    public TextNode parent, left, right;
    public boolean isEdge;
    public int x, depth;

    public TextNode(String text){
        this.text = text;
        parent = null; left = null; right = null;
        isEdge = false;
        x = 0; depth = 0;
    }
}

Cuối cùng, đây là lớp kiểm tra để in mẫu đã cho:


public class Test {

    public static void main(String[] args){
        TextNode root = new TextNode("+");
        root.left = new TextNode("*");            root.left.parent = root;
        root.right = new TextNode("-");           root.right.parent = root;
        root.left.left = new TextNode("speed");   root.left.left.parent = root.left;
        root.left.right = new TextNode("2");      root.left.right.parent = root.left;
        root.right.left = new TextNode("45");     root.right.left.parent = root.right;
        root.right.right = new TextNode("12");    root.right.right.parent = root.right;

        System.out.println(TreePrinter.TreeString(root));
    }
}

1

Bạn có thể sử dụng một applet để hình dung điều này rất dễ dàng. Bạn cần in các mục sau.

  1. In các nút dưới dạng vòng tròn với một số bán kính hiển thị

    • Lấy tọa độ cho mỗi nút.

    • Tọa độ x có thể được hiển thị dưới dạng số lượng nút được truy cập trước khi nút được truy cập trong giao dịch theo thứ tự.

    • Tọa độ y có thể được hình dung như độ sâu của nút cụ thể.


  1. In các dòng giữa cha mẹ và con cái

    • Điều này có thể được thực hiện bằng cách duy trì tọa độ x và y của các nút và cha mẹ của mỗi nút trong các danh sách riêng biệt.

    • Đối với mỗi nút ngoại trừ gốc, nối mỗi nút với cha của nó bằng cách lấy tọa độ x và y của cả con và cha.


bạn có thể vui lòng hình dung và đưa ra một giải pháp tốt hơn các câu trả lời hiện có không?
Menul Hassan

1
private StringBuilder prettyPrint(Node root, int currentHeight, int totalHeight) {
        StringBuilder sb = new StringBuilder();
        int spaces = getSpaceCount(totalHeight-currentHeight + 1);
        if(root == null) {
            //create a 'spatial' block and return it
            String row = String.format("%"+(2*spaces+1)+"s%n", "");
            //now repeat this row space+1 times
            String block = new String(new char[spaces+1]).replace("\0", row);
            return new StringBuilder(block);
        }
        if(currentHeight==totalHeight) return new StringBuilder(root.data+"");
        int slashes = getSlashCount(totalHeight-currentHeight +1);
        sb.append(String.format("%"+(spaces+1)+"s%"+spaces+"s", root.data+"", ""));
        sb.append("\n");
        //now print / and \
        // but make sure that left and right exists
        char leftSlash = root.left == null? ' ':'/';
        char rightSlash = root.right==null? ' ':'\\';
        int spaceInBetween = 1;
        for(int i=0, space = spaces-1; i<slashes; i++, space --, spaceInBetween+=2) {
            for(int j=0; j<space; j++) sb.append(" ");
            sb.append(leftSlash);
            for(int j=0; j<spaceInBetween; j++) sb.append(" ");
            sb.append(rightSlash+"");
            for(int j=0; j<space; j++) sb.append(" ");
            sb.append("\n");
        }
        //sb.append("\n");

        //now get string representations of left and right subtrees
        StringBuilder leftTree = prettyPrint(root.left, currentHeight+1, totalHeight);
        StringBuilder rightTree = prettyPrint(root.right, currentHeight+1, totalHeight);
        // now line by line print the trees side by side
        Scanner leftScanner = new Scanner(leftTree.toString());
        Scanner rightScanner = new Scanner(rightTree.toString());
//      spaceInBetween+=1;
        while(leftScanner.hasNextLine()) {
            if(currentHeight==totalHeight-1) {
                sb.append(String.format("%-2s %2s", leftScanner.nextLine(), rightScanner.nextLine()));
                sb.append("\n");
                spaceInBetween-=2;              
            }
            else {
                sb.append(leftScanner.nextLine());
                sb.append(" ");
                sb.append(rightScanner.nextLine()+"\n");
            }
        }

        return sb;

    }
private int getSpaceCount(int height) {
        return (int) (3*Math.pow(2, height-2)-1);
    }
private int getSlashCount(int height) {
        if(height <= 3) return height -1;
        return (int) (3*Math.pow(2, height-3)-1);
    }

https://github.com/murtraja/java-binary-tree-printer

chỉ hoạt động cho các số nguyên 1 đến 2 chữ số (tôi rất lười để làm cho nó chung chung)

xiên đầy


1

Đây là giải pháp đơn giản nhất cho chế độ xem ngang. Đã thử với một loạt các ví dụ. Hoạt động tốt cho mục đích của tôi. Cập nhật từ câu trả lời của @ nitin-k.

public void print(String prefix, BTNode n, boolean isLeft) {
    if (n != null) {
        print(prefix + "     ", n.right, false);
        System.out.println (prefix + ("|-- ") + n.data);
        print(prefix + "     ", n.left, true);
    }
}

Gọi:

bst.print("", bst.root, false);

Giải pháp:

                         |-- 80
                    |-- 70
               |-- 60
          |-- 50
     |-- 40
|-- 30
     |-- 20
          |-- 10

1
  1. Bạn sẽ cần phải cấp thứ tự đi ngang qua cây của bạn.
  2. Chọn chiều dài nútchiều dài không gian .
  3. Lấy cây cơ sở chiều rộng tương đối với nhau mức đó là node_length * nodes_count + space_length * spaces_count*.
  4. Tìm mối quan hệ giữa phân nhánh, khoảng cách, thụt lề và chiều rộng cơ sở tính toán.

Mã trên GitHub: YoussefRaafatNasry / bst-ascii-visualization

                                             07                     
                                             /\                     
                                            /  \                    
                                           /    \                   
                                          /      \                  
                                         /        \                 
                                        /          \                
                                       /            \               
                                      /              \              
                                     /                \             
                                    /                  \            
                                   /                    \           
                                 03                      11         
                                 /\                      /\         
                                /  \                    /  \        
                               /    \                  /    \       
                              /      \                /      \      
                             /        \              /        \     
                           01          05          09          13   
                           /\          /\          /\          /\   
                          /  \        /  \        /  \        /  \  
                        00    02    04    06    08    10    12    14

Tôi muốn nói rằng mã này đủ ngắn để bạn có thể nhúng nó vào câu trả lời của mình.
m02ph3u5

Mã không chỉ là visualizechức năng, nó là toàn bộ visualizerlớp có khoảng 200 loc bao gồm cả tệp tiêu đề.
YoussefRaafatNasry

1

Đối với những người tìm kiếm giải pháp Rust:

pub struct Node {
  pub value: i32,
  left: Option<Box<Node>>,
  right: Option<Box<Node>>
}

impl Node {

  pub fn new(val: i32) -> Node {
    Node {
      value: val,
      left: None,
      right: None
    }
  }

  pub fn getLeftNode(&self) -> Option<&Node> {
   self.left.as_deref()
  }

  pub fn getRightNode(&self) -> Option<&Node> {
   self.right.as_deref()
  }

  pub fn setLeftNode(&mut self, val: i32) -> &mut Node {
   self.left = Some(Box::new(Node::new(val)));
   self.left.as_deref_mut().unwrap()
  }

  pub fn setRightNode(&mut self, val: i32) -> &mut Node {
   self.right = Some(Box::new(Node::new(val)));
   self.right.as_deref_mut().unwrap()
  }

  fn visualizeTree(&self, level: u16, is_tail: bool, columns: &mut HashSet<u16>) {
    let left = self.getLeftNode();
    let right = self.getRightNode();

    if right.is_some() {
      right.unwrap().visualizeTree(level+1, false, columns);
    }

    if level > 0 {
      for i in 0..level-1 {
          if columns.contains(&i) {
            print!("│   ");
          } else {
            print!("    ");
          }
      }
      if is_tail {
        println!("└── {}", self.value);
        columns.remove(&(level-1));
        columns.insert(level);
      } else {
        println!("┌── {}", self.value);
        columns.insert(level);
        columns.insert(level-1);
      }
    } else {
      println!("{}", self.value);
    }

    if left.is_some() {
      left.unwrap().visualizeTree(level+1, true, columns);
    }
  }

  pub fn printTree(&self) {
    let mut columns = HashSet::new();
    columns.insert(0);
    self.visualizeTree(0, true, &mut columns);
  }
}

Đầu ra là một cái gì đó như thế này:

┌── 17
      ┌── 3
         └── 9
   └── 2
       └── 1
20
   ┌── 7
         ┌── 16
      └── 15
└── 8
       ┌── 11
    └── 4
        └── 13

0

In trong Bảng điều khiển:

                                                500
                       700                                             300   
    200                                   400                                                                                          

Mã đơn giản:

public int getHeight()
    {
        if(rootNode == null) return -1;
        return getHeight(rootNode);
    }

    private int getHeight(Node node)
    {
        if(node == null) return -1;

        return Math.max(getHeight(node.left), getHeight(node.right)) + 1;
    }

    public void printBinaryTree(Node rootNode)
    {
        Queue<Node> rootsQueue = new LinkedList<Node>();
        Queue<Node> levelQueue = new LinkedList<Node>();
        levelQueue.add(rootNode);
        int treeHeight = getHeight();
        int firstNodeGap;
        int internalNodeGap;
        int copyinternalNodeGap;
        while(true)
        {
            System.out.println("");
            internalNodeGap = (int)(Math.pow(2, treeHeight + 1) -1);  
            copyinternalNodeGap = internalNodeGap;
            firstNodeGap = internalNodeGap/2;

            boolean levelFirstNode = true;

            while(!levelQueue.isEmpty())
            {
                internalNodeGap = copyinternalNodeGap;
                Node currNode = levelQueue.poll();
                if(currNode != null)
                {
                    if(levelFirstNode)
                    {
                        while(firstNodeGap > 0)
                        {
                            System.out.format("%s", "   ");
                            firstNodeGap--; 
                        }
                        levelFirstNode =false;
                    }
                    else
                    {
                        while(internalNodeGap>0)
                        {
                            internalNodeGap--;
                            System.out.format("%s", "   ");
                        }
                    }
                    System.out.format("%3d",currNode.data);
                    rootsQueue.add(currNode);
                }
            }

            --treeHeight;

            while(!rootsQueue.isEmpty())
            {
                Node currNode = rootsQueue.poll();
                if(currNode != null)
                {
                    levelQueue.add(currNode.left);
                    levelQueue.add(currNode.right);
                }
            }

            if(levelQueue.isEmpty()) break;
        }

    }

0

Đây là một máy in cây rất linh hoạt. Không phải là tìm kiếm tốt nhất, nhưng nó xử lý rất nhiều trường hợp. Hãy thêm dấu gạch chéo nếu bạn có thể tìm ra. nhập mô tả hình ảnh ở đây

package com.tomac120.NodePrinter;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by elijah on 6/28/16.
 */
public class NodePrinter{
    final private List<List<PrintableNodePosition>> nodesByRow;
    int maxColumnsLeft = 0;
    int maxColumnsRight = 0;
    int maxTitleLength = 0;
    String sep = " ";
    int depth = 0;

    public NodePrinter(PrintableNode rootNode, int chars_per_node){
        this.setDepth(rootNode,1);
        nodesByRow = new ArrayList<>(depth);
        this.addNode(rootNode._getPrintableNodeInfo(),0,0);
        for (int i = 0;i<chars_per_node;i++){
            //sep += " ";
        }
    }

    private void setDepth(PrintableNode info, int depth){
        if (depth > this.depth){
            this.depth = depth;
        }
        if (info._getLeftChild() != null){
            this.setDepth(info._getLeftChild(),depth+1);
        }
        if (info._getRightChild() != null){
            this.setDepth(info._getRightChild(),depth+1);
        }
    }

    private void addNode(PrintableNodeInfo node, int level, int position){
        if (position < 0 && -position > maxColumnsLeft){
            maxColumnsLeft = -position;
        }
        if (position > 0 && position > maxColumnsRight){
            maxColumnsRight = position;
        }
        if (node.getTitleLength() > maxTitleLength){
           maxTitleLength = node.getTitleLength();
        }
        List<PrintableNodePosition> row = this.getRow(level);
        row.add(new PrintableNodePosition(node, level, position));
        level++;

        int depthToUse = Math.min(depth,6);
        int levelToUse = Math.min(level,6);
        int offset = depthToUse - levelToUse-1;
        offset = (int)(Math.pow(offset,Math.log(depthToUse)*1.4));
        offset = Math.max(offset,3);


        PrintableNodeInfo leftChild = node.getLeftChildInfo();
        PrintableNodeInfo rightChild = node.getRightChildInfo();
        if (leftChild != null){
            this.addNode(leftChild,level,position-offset);
        }
        if (rightChild != null){
            this.addNode(rightChild,level,position+offset);
        }
    }

    private List<PrintableNodePosition> getRow(int row){
        if (row > nodesByRow.size() - 1){
            nodesByRow.add(new LinkedList<>());
        }
        return nodesByRow.get(row);
    }

    public void print(){
        int max_chars = this.maxColumnsLeft+maxColumnsRight+1;
        int level = 0;
        String node_format = "%-"+this.maxTitleLength+"s";
        for (List<PrintableNodePosition> pos_arr : this.nodesByRow){
            String[] chars = this.getCharactersArray(pos_arr,max_chars);
            String line = "";
            int empty_chars = 0;
            for (int i=0;i<chars.length+1;i++){
                String value_i = i < chars.length ? chars[i]:null;
                if (chars.length + 1 == i || value_i != null){
                    if (empty_chars > 0) {
                        System.out.print(String.format("%-" + empty_chars + "s", " "));
                    }
                    if (value_i != null){
                        System.out.print(String.format(node_format,value_i));
                        empty_chars = -1;
                    } else{
                        empty_chars = 0;
                    }
                } else {
                    empty_chars++;
                }
            }
            System.out.print("\n");

            int depthToUse = Math.min(6,depth);
            int line_offset = depthToUse - level;
            line_offset *= 0.5;
            line_offset = Math.max(0,line_offset);

            for (int i=0;i<line_offset;i++){
                System.out.println("");
            }


            level++;
        }
    }

    private String[] getCharactersArray(List<PrintableNodePosition> nodes, int max_chars){
        String[] positions = new String[max_chars+1];
        for (PrintableNodePosition a : nodes){
            int pos_i = maxColumnsLeft + a.column;
            String title_i = a.nodeInfo.getTitleFormatted(this.maxTitleLength);
            positions[pos_i] = title_i;
        }
        return positions;
    }
}

Lớp NodeInfo

package com.tomac120.NodePrinter;

/**
 * Created by elijah on 6/28/16.
 */
public class PrintableNodeInfo {
    public enum CLI_PRINT_COLOR {
        RESET("\u001B[0m"),
        BLACK("\u001B[30m"),
        RED("\u001B[31m"),
        GREEN("\u001B[32m"),
        YELLOW("\u001B[33m"),
        BLUE("\u001B[34m"),
        PURPLE("\u001B[35m"),
        CYAN("\u001B[36m"),
        WHITE("\u001B[37m");

        final String value;
        CLI_PRINT_COLOR(String value){
            this.value = value;
        }

        @Override
        public String toString() {
            return value;
        }
    }
    private final String title;
    private final PrintableNode leftChild;
    private final PrintableNode rightChild;
    private final CLI_PRINT_COLOR textColor;

    public PrintableNodeInfo(String title, PrintableNode leftChild, PrintableNode rightChild){
        this(title,leftChild,rightChild,CLI_PRINT_COLOR.BLACK);
    }

    public PrintableNodeInfo(String title, PrintableNode leftChild, PrintableNode righthild, CLI_PRINT_COLOR textColor){
        this.title = title;
        this.leftChild = leftChild;
        this.rightChild = righthild;
        this.textColor = textColor;
    }

    public String getTitle(){
        return title;
    }

    public CLI_PRINT_COLOR getTextColor(){
        return textColor;
    }

    public String getTitleFormatted(int max_chars){
        return this.textColor+title+CLI_PRINT_COLOR.RESET;
        /*
        String title = this.title.length() > max_chars ? this.title.substring(0,max_chars+1):this.title;
        boolean left = true;
        while(title.length() < max_chars){
            if (left){
                title = " "+title;
            } else {
                title = title + " ";
            }
        }
        return this.textColor+title+CLI_PRINT_COLOR.RESET;*/
    }

    public int getTitleLength(){
        return title.length();
    }

    public PrintableNodeInfo getLeftChildInfo(){
        if (leftChild == null){
            return null;
        }
        return leftChild._getPrintableNodeInfo();
    }

    public PrintableNodeInfo getRightChildInfo(){
        if (rightChild == null){
            return null;
        }
        return rightChild._getPrintableNodeInfo();
    }
}

Lớp NodePocation

package com.tomac120.NodePrinter;

/**
 * Created by elijah on 6/28/16.
 */
public class PrintableNodePosition implements Comparable<PrintableNodePosition> {
    public final int row;
    public final int column;
    public final PrintableNodeInfo nodeInfo;
    public PrintableNodePosition(PrintableNodeInfo nodeInfo, int row, int column){
        this.row = row;
        this.column = column;
        this.nodeInfo = nodeInfo;
    }

    @Override
    public int compareTo(PrintableNodePosition o) {
        return Integer.compare(this.column,o.column);
    }
}

Và cuối cùng, Giao diện nút

package com.tomac120.NodePrinter;

/**
 * Created by elijah on 6/28/16.
 */
public interface PrintableNode {
    PrintableNodeInfo _getPrintableNodeInfo();
    PrintableNode _getLeftChild();
    PrintableNode _getRightChild();
}

0

Một giải pháp Scala, được điều chỉnh từ câu trả lời của Vasya Novikov và chuyên về cây nhị phân:

/** An immutable Binary Tree. */
case class BTree[T](value: T, left: Option[BTree[T]], right: Option[BTree[T]]) {

  /* Adapted from: http://stackoverflow.com/a/8948691/643684 */
  def pretty: String = {
    def work(tree: BTree[T], prefix: String, isTail: Boolean): String = {
      val (line, bar) = if (isTail) ("└── ", " ") else ("├── ", "│")

      val curr = s"${prefix}${line}${tree.value}"

      val rights = tree.right match {
        case None    => s"${prefix}${bar}   ├── ∅"
        case Some(r) => work(r, s"${prefix}${bar}   ", false)
      }

      val lefts = tree.left match {
        case None    => s"${prefix}${bar}   └── ∅"
        case Some(l) => work(l, s"${prefix}${bar}   ", true)
      }

      s"${curr}\n${rights}\n${lefts}"

    }

    work(this, "", true)
  }
}

BTW, tôi cũng đã quyết định đăng một giải pháp Scala: stackoverflow.com/a/43336145/1091436
VasiliNovikov

0

Xem thêm những câu trả lời .

Đặc biệt, không quá khó để sử dụng abego TreeLayout để tạo kết quả hiển thị bên dưới với các cài đặt mặc định.

Nếu bạn thử công cụ đó, hãy lưu ý cảnh báo này: Nó in trẻ em theo thứ tự chúng đã được thêm vào. Đối với một BST trong đó các vấn đề trái và phải tôi thấy thư viện này không phù hợp mà không sửa đổi.

Ngoài ra, phương thức để thêm con chỉ cần lấy một nút parentchildlàm tham số. (Vì vậy, để xử lý một loạt các nút, bạn phải tách riêng nút đầu tiên để tạo gốc.)

Tôi đã kết thúc bằng cách sử dụng giải pháp này ở trên, sửa đổi nó thành loại <Node>để có quyền truy cập vào Nodebên trái và bên phải (trẻ em).

cây được tạo bằng abego TreeLayout


0

Đây là một cách khác để trực quan hóa cây của bạn: lưu các nút dưới dạng tệp xml và sau đó để trình duyệt của bạn hiển thị cho bạn cấu trúc phân cấp:

class treeNode{
    int key;
    treeNode left;
    treeNode right;

    public treeNode(int key){
        this.key = key;
        left = right = null;
    }

    public void printNode(StringBuilder output, String dir){
        output.append("<node key='" + key + "' dir='" + dir + "'>");
        if(left != null)
            left.printNode(output, "l");
        if(right != null)
            right.printNode(output, "r");
        output.append("</node>");
    }
}

class tree{
    private treeNode treeRoot;

    public tree(int key){
        treeRoot = new treeNode(key);
    }

    public void insert(int key){
        insert(treeRoot, key);
    }

    private treeNode insert(treeNode root, int key){
        if(root == null){
            treeNode child = new treeNode(key);
            return child;
        }

        if(key < root.key)
            root.left = insert(root.left, key);
        else if(key > root.key)
            root.right = insert(root.right, key);

        return root;
    }

    public void saveTreeAsXml(){
        StringBuilder strOutput = new StringBuilder();
        strOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        treeRoot.printNode(strOutput, "root");
        try {
            PrintWriter writer = new PrintWriter("C:/tree.xml", "UTF-8");
            writer.write(strOutput.toString());
            writer.close();
        }
        catch (FileNotFoundException e){

        }
        catch(UnsupportedEncodingException e){

        }
    }
}

Đây là mã để kiểm tra nó:

    tree t = new tree(1);
    t.insert(10);
    t.insert(5);
    t.insert(4);
    t.insert(20);
    t.insert(40);
    t.insert(30);
    t.insert(80);
    t.insert(60);
    t.insert(50);

    t.saveTreeAsXml();

Và đầu ra trông như thế này:

nhập mô tả hình ảnh ở đây


0
using map...
{
Map<Integer,String> m = new LinkedHashMap<>();

         tn.printNodeWithLvl(node,l,m);

        for(Entry<Integer, String> map :m.entrySet()) {
            System.out.println(map.getValue());
        }
then....method


   private  void printNodeWithLvl(Node node,int l,Map<Integer,String> m) {
       if(node==null) {
           return;
       }
      if(m.containsKey(l)) {
          m.put(l, new StringBuilder(m.get(l)).append(node.value).toString());
      }else {
          m.put(l, node.value+"");
      }
      l++;
      printNodeWithLvl( node.left,l,m);
      printNodeWithLvl(node.right,l,m);

    }
}

0

đây là một trong những phiên bản đơn giản nhất tôi có thể thực hiện. Tôi hy vọng nó sẽ giúp bạn

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

    def add(self, data):

        if data < self.data:
            if self.left is None:
                self.left = Node(data)
            else:
                self.left.add(data)
        if data > self.data:
            if self.right is None:
                self.right = Node(data)
            else:
                self.right.add(data)

    def display(self):
        diff = 16
        start = 50
        c = ' '

        this_level = [(self, start)]

        while this_level:
            next_level = list()
            last_line = ''

            for node, d in this_level:
                line = last_line + c*(d - len(last_line)) + str(node.data)
                print(line, end='\r')
                last_line = line

                if node.left:
                    next_level.append((node.left, d - diff))
                if node.right:
                    next_level.append((node.right, d + diff))
                this_level = next_level
                diff = max(diff//2, 2)
            print('\n')


if __name__ == '__main__':
    from random import randint, choice
    values = [randint(0, 100) for _ in range(10)]
    bst = Node(choice(values))
    for data in values:
        bst.add(data)

    bst.display()

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.