24

只是想知道我是否可以获得一些关于以以下形式打印漂亮二叉树的提示:

5
     10
          11
          7
               6
     3
          4
          2

现在它打印的是:

   2
    4
    3 
    6
    7
    11
    10
    5

我知道我的示例与我当前正在打印的内容是颠倒的,如果我从当前打印的根目录向下打印,这并不重要。任何提示都非常感谢我的完整问题:

如何修改我的打印以使树看起来像一棵树?

    //Binary Search Tree Program

#include <iostream>
#include <cstdlib>
#include <queue>
using namespace std;

int i = 0;

class BinarySearchTree
{
   private:
   struct tree_node
   {
      tree_node* left;
      tree_node* right;
      int data;
   };
   tree_node* root;

   public:
   BinarySearchTree()
   {
      root = NULL;
   }

   bool isEmpty() const { return root==NULL; }
   void print_inorder();
   void inorder(tree_node*);
   void print_preorder();
   void preorder(tree_node*);
   void print_postorder();
   void postorder(tree_node*);
   void insert(int);
   void remove(int);
};

// Smaller elements go left
// larger elements go right
void BinarySearchTree::insert(int d)
{
   tree_node* t = new tree_node;
   tree_node* parent;
   t->data = d;
   t->left = NULL;
   t->right = NULL;
   parent = NULL;

   // is this a new tree?
   if(isEmpty()) root = t;
   else
   {
      //Note: ALL insertions are as leaf nodes
      tree_node* curr;
      curr = root;
      // Find the Node's parent
      while(curr)
      {
         parent = curr;
         if(t->data > curr->data) curr = curr->right;
         else curr = curr->left;
      }

      if(t->data < parent->data)
      {
         parent->left = t;
      }
      else
      {
      parent->right = t;
      }
    }
}

void BinarySearchTree::remove(int d)
{
   //Locate the element
   bool found = false;
   if(isEmpty())
   {
      cout<<" This Tree is empty! "<<endl;
      return;
   }

   tree_node* curr;
   tree_node* parent;
   curr = root;

   while(curr != NULL)
   {
      if(curr->data == d)
      {
         found = true;
         break;
      }
      else
      {
         parent = curr;
         if(d>curr->data) curr = curr->right;
         else curr = curr->left;
      }
    }
    if(!found)
    {
      cout<<" Data not found! "<<endl;
      return;
    }


    // 3 cases :
    // 1. We're removing a leaf node
    // 2. We're removing a node with a single child
    // 3. we're removing a node with 2 children

    // Node with single child
    if((curr->left == NULL && curr->right != NULL) || (curr->left != NULL && curr->right == NULL))
    {
      if(curr->left == NULL && curr->right != NULL)
      {
         if(parent->left == curr)
         {
            parent->left = curr->right;
            delete curr;
         }
         else
         {
            parent->right = curr->left;
            delete curr;
         }
       }
       return;
    }

    //We're looking at a leaf node
    if( curr->left == NULL && curr->right == NULL)
    {
      if(parent->left == curr)
      {
         parent->left = NULL;
      }
      else
      {
         parent->right = NULL;
      }
      delete curr;
      return;
    }


    //Node with 2 children
    // replace node with smallest value in right subtree
    if (curr->left != NULL && curr->right != NULL)
    {
       tree_node* chkr;
       chkr = curr->right;
       if((chkr->left == NULL) && (chkr->right == NULL))
       {
         curr = chkr;
         delete chkr;
         curr->right = NULL;
       }
       else // right child has children
       {
         //if the node's right child has a left child
         // Move all the way down left to locate smallest element

         if((curr->right)->left != NULL)
         {
            tree_node* lcurr;
            tree_node* lcurrp;
            lcurrp = curr->right;
            lcurr = (curr->right)->left;
            while(lcurr->left != NULL)
            {
               lcurrp = lcurr;
               lcurr = lcurr->left;
            }
            curr->data = lcurr->data;
            delete lcurr;
            lcurrp->left = NULL;
         }
         else
         {
            tree_node* tmp;
            tmp = curr->right;
            curr->data = tmp->data;
            curr->right = tmp->right;
            delete tmp;
         }

      }
      return;
   }

}
void BinarySearchTree::print_postorder()
{
   postorder(root);
}

void BinarySearchTree::postorder(tree_node* p)
{
   if(p != NULL)
   {
      if(p->left) postorder(p->left);
      if(p->right) postorder(p->right);
      cout<<"     "<<p->data<<"\n ";
   }
   else return;
}

int main()
{
    BinarySearchTree b;
    int ch,tmp,tmp1;
    while(1)
    {
       cout<<endl<<endl;
       cout<<" Binary Search Tree Operations "<<endl;
       cout<<" ----------------------------- "<<endl;
       cout<<" 1. Insertion/Creation "<<endl;
       cout<<" 2. Printing "<<endl;
       cout<<" 3. Removal "<<endl;
       cout<<" 4. Exit "<<endl;
       cout<<" Enter your choice : ";
       cin>>ch;
       switch(ch)
       {
           case 1 : cout<<" Enter Number to be inserted : ";
                    cin>>tmp;
                    b.insert(tmp);
                    i++;
                    break;
           case 2 : cout<<endl;
                    cout<<" Printing "<<endl;
                    cout<<" --------------------"<<endl;
                    b.print_postorder();
                    break;
           case 3 : cout<<" Enter data to be deleted : ";
                    cin>>tmp1;
                    b.remove(tmp1);
                    break;
           case 4:
                    return 0;

       }
    }
}
4

16 回答 16

17

为了递归地漂亮打印一棵树,您需要将两个参数传递给您的打印函数:

  • 要打印的树节点,以及
  • 缩进级别

例如,您可以这样做:

void BinarySearchTree::postorder(tree_node* p, int indent=0)
{
    if(p != NULL) {
        if(p->left) postorder(p->left, indent+4);
        if(p->right) postorder(p->right, indent+4);
        if (indent) {
            std::cout << std::setw(indent) << ' ';
        }
        cout<< p->data << "\n ";
    }
}

最初的电话应该是postorder(root);

如果您想打印根在顶部的树,请cout移至if.

于 2012-11-21T01:24:15.940 回答
15
void btree::postorder(node* p, int indent)
{
    if(p != NULL) {
        if(p->right) {
            postorder(p->right, indent+4);
        }
        if (indent) {
            std::cout << std::setw(indent) << ' ';
        }
        if (p->right) std::cout<<" /\n" << std::setw(indent) << ' ';
        std::cout<< p->key_value << "\n ";
        if(p->left) {
            std::cout << std::setw(indent) << ' ' <<" \\\n";
            postorder(p->left, indent+4);
        }
    }
}

有了这棵树:

btree *mytree = new btree();
mytree->insert(2);
mytree->insert(1);
mytree->insert(3);
mytree->insert(7);
mytree->insert(10);
mytree->insert(2);
mytree->insert(5);
mytree->insert(8);
mytree->insert(6);
mytree->insert(4);
mytree->postorder(mytree->root);

会导致这样的结果:

在此处输入图像描述

于 2014-11-02T13:49:06.500 回答
6

它永远不会足够漂亮,除非你做一些回溯来重新校准显示输出。但是可以使用启发式有效地发出足够多的二叉树:给定一棵树的高度,可以猜测不同深度的节点的预期宽度和集合是多少。有几部分需要做到这一点,所以让我们首先从更高级别的函数开始提供上下文。

漂亮的打印功能:

   // create a pretty vertical tree
   void postorder(Node *p)
   {
      int height = getHeight(p) * 2;
      for (int i = 0 ; i < height; i ++) {
         printRow(p, height, i);
      }
   }

上面的代码很简单。主要逻辑在 printRow 函数中。让我们深入研究一下。

void printRow(const Node *p, const int height, int depth)
{
        vector<int> vec;
        getLine(p, depth, vec);
        cout << setw((height - depth)*2); // scale setw with depth
        bool toggle = true; // start with left
        if (vec.size() > 1) {
                for (int v : vec) {
                        if (v != placeholder) {
                                if (toggle)
                                        cout << "/" << "   ";
                                else
                                        cout << "\\" << "   ";
                        }
                        toggle = !toggle;
                }
                cout << endl;
                cout << setw((height - depth)*2);
        }
        for (int v : vec) {
                if (v != placeholder)
                        cout << v << "   ";
        }
        cout << endl;
}

getLine() 符合您的预期:它将具有给定相等深度的所有节点存储到 vec 中。这是代码:

void getLine(const Node *root, int depth, vector<int>& vals)
{
        if (depth <= 0 && root != nullptr) {
                vals.push_back(root->val);
                return;
        }
        if (root->left != nullptr)
                getLine(root->left, depth-1, vals);
        else if (depth-1 <= 0)
                vals.push_back(placeholder);
        if (root->right != nullptr)
                getLine(root->right, depth-1, vals);
        else if (depth-1 <= 0)
                vals.push_back(placeholder);
}

现在回到 printRow()。对于每一行,我们根据我们在二叉树中的深度来设置流宽度。这种格式会很好,因为通常情况下,你走得越深,需要的宽度就越大。我说通常是因为在退化的树中,这看起来不那么漂亮。只要树大致平衡且较小(< 20 个项目),结果应该很好。需要一个占位符来正确对齐“/”和“\”字符。因此,当通过 getLine() 获得一行时,如果在指定深度处不存在任何节点,我们将插入占位符。占位符可以设置为类似(1<<31)例如。显然,这并不可靠,因为占位符可能是有效的节点值。如果编码员有胆量并且只处理小数,则可以修改代码以通过 getLine() 发出十进制转换的字符串并使用“_”之类的占位符。(不幸的是,我不是这样的编码员:P)

以下项目按顺序插入的结果:8、12、4、2、5、15 是

       8   
     /   \   
     4   12   
   /   \   \   
   2   5   15   

getHeight() 留给读者作为练习。:) 甚至可以通过根据较深节点中的项目数追溯更新浅节点的集合来获得更漂亮的结果。这也留给读者作为练习。

于 2015-06-15T03:47:26.023 回答
5
    //Binary tree (pretty print):
    //                        ________________________50______________________                        
    //            ____________30                                  ____________70__________            
    //      ______20____                                          60                ______90          
    //      10          15                                                          80                


    // prettyPrint
    public static void prettyPrint(BTNode node) {
        // get height first
        int height = heightRecursive(node);

        // perform  level order traversal
        Queue<BTNode> queue = new LinkedList<BTNode>();

        int level = 0;
        final int SPACE = 6;
        int nodePrintLocation = 0;

        // special node for pushing when a node has no left or right child (assumption, say this node is a node with value Integer.MIN_VALUE)
        BTNode special = new BTNode(Integer.MIN_VALUE);

        queue.add(node);
        queue.add(null);    // end of level 0
        while(! queue.isEmpty()) {
            node = queue.remove();

            if (node == null) {
                if (!queue.isEmpty()) {
                    queue.add(null);
                }

                // start of new level
                System.out.println();
                level++;
            } else {
                nodePrintLocation = ((int) Math.pow(2, height - level)) * SPACE;

                System.out.print(getPrintLine(node, nodePrintLocation));

                if (level < height) {
                    // only go till last level
                    queue.add((node.left != null) ? node.left : special);
                    queue.add((node.right != null) ? node.right : special);
                }
            }
        }       
    }
    public void prettyPrint() {
        System.out.println("\nBinary tree (pretty print):");
        prettyPrint(root);
    }
    private static String getPrintLine(BTNode node, int spaces) {
        StringBuilder sb = new StringBuilder();

        if (node.data == Integer.MIN_VALUE) {
            // for child nodes, print spaces
            for (int i = 0; i < 2 * spaces; i++) {
                sb.append(" ");
            }

            return sb.toString();
        }

        int i = 0;
        int to = spaces/2;

        for (; i < to; i++) {
            sb.append(' ');
        }
        to += spaces/2;
        char ch = ' ';
        if (node.left != null) {
            ch = '_';
        }
        for (; i < to; i++) {
            sb.append(ch);
        }

        String value = Integer.toString(node.data);
        sb.append(value);

        to += spaces/2;
        ch = ' ';
        if (node.right != null) {
            ch = '_';
        }
        for (i += value.length(); i < to; i++) {
            sb.append(ch);
        }

        to += spaces/2;
        for (; i < to; i++) {
            sb.append(' ');
        }

        return sb.toString();
    }

    private static int heightRecursive(BTNode  node) {
        if (node == null) {
            // empty tree
            return -1;
        }

        if (node.left == null && node.right == null) {
            // leaf node
            return 0;
        }

        return 1 + Math.max(heightRecursive(node.left), heightRecursive(node.right));
    }
于 2015-07-29T21:09:14.323 回答
5
#include <stdio.h>
#include <stdlib.h>

struct Node
{
    struct Node *left,*right;
    int val;
} *root=NULL;

int rec[1000006];
void addNode(int,struct Node*);
void printTree(struct Node* curr,int depth)
{
    int i;
    if(curr==NULL)return;
    printf("\t");
    for(i=0;i<depth;i++)
        if(i==depth-1)
            printf("%s\u2014\u2014\u2014",rec[depth-1]?"\u0371":"\u221F");
        else
            printf("%s   ",rec[i]?"\u23B8":"  ");
    printf("%d\n",curr->val);
    rec[depth]=1;
    printTree(curr->left,depth+1);
    rec[depth]=0;
    printTree(curr->right,depth+1);
}
int main()
{
    root=(struct Node*)malloc(sizeof(struct Node));
    root->val=50;
    //addNode(50,root);
    addNode(75,root);    addNode(25,root);
    addNode(15,root);    addNode(30,root);
    addNode(100,root);    addNode(60,root);
    addNode(27,root);    addNode(31,root);
    addNode(101,root);    addNode(99,root);
    addNode(5,root);    addNode(61,root);
    addNode(55,root);    addNode(20,root);
    addNode(0,root);    addNode(21,root);
    //deleteNode(5,root);

    printTree(root,0);
    return 0;
}

void addNode(int v,struct Node* traveller)
{
    struct Node *newEle=(struct Node*)malloc(sizeof(struct Node));
    newEle->val=v;
    for(;;)
    {
        if(v<traveller->val)
        {
            if(traveller->left==NULL){traveller->left=newEle;return;}
            traveller=traveller->left;
        }
        else if(v>traveller->val)
        {
            if(traveller->right==NULL){traveller->right=newEle;return;}
            traveller=traveller->right;
        }
        else
        {
            printf("%d Input Value is already present in the Tree !!!\n",v);
            return;
        }
    }
}

希望,你觉得它很漂亮...

输出:

50
ͱ———25
⎸   ͱ———15
⎸   ⎸   ͱ———5
⎸   ⎸   ⎸   ͱ———0
⎸   ⎸   ∟———20
⎸   ⎸        ∟———21
⎸   ∟———30
⎸        ͱ———27
⎸        ∟———31
∟———75
     ͱ———60
     ⎸   ͱ———55
     ⎸   ∟———61
     ∟———100
          ͱ———99
          ∟———101
于 2017-11-20T20:33:42.237 回答
3

如果您只需要可视化您的树,更好的方法是将其输出为点格式并使用 grapviz 进行绘制。

您可以查看点指南以获取更多信息 abt 语法等

于 2012-11-21T01:20:33.657 回答
2

这是一个以树形式打印基于数组的堆的小示例。对于更大的数字,它需要对算法进行一些调整。我只是在纸上做了一个网格,并计算出每个节点看起来不错的空间索引,然后注意到每个节点需要多少空间,基于其父节点的空间数量和递归级别以及如何树很高。该解决方案超越了仅按水平顺序打印并满足“美观”要求。

#include <iostream>
#include <vector>

static const int g_TerminationNodeValue = -999;

class HeapJ
{
public:
HeapJ(int* pHeapArray, int numElements)
{
    m_pHeapPointer = pHeapArray;
    m_numElements = numElements;

    m_treeHeight = GetTreeHeight(1);
}

void Print()
{
    m_printVec.clear();

    int initialIndex = 0;
    for(int i=1; i<m_treeHeight; ++i)
    {
        int powerOfTwo = 1;
        for(int j=0; j<i; ++j)
        {
            powerOfTwo *= 2;
        }

        initialIndex += powerOfTwo - (i-1);
    }

    DoPrintHeap(1,0,initialIndex);

    for(size_t i=0; i<m_printVec.size(); ++i)
    {
        std::cout << m_printVec[i] << '\n' << '\n';
    }
}

private:
int* m_pHeapPointer;
int m_numElements;
int m_treeHeight;
std::vector<std::string> m_printVec;

int GetTreeHeight(int index)
{
    const int value = m_pHeapPointer[index-1];

    if(value == g_TerminationNodeValue)
    {
        return -1;
    }

    const int childIndexLeft = 2*index;
    const int childIndexRight = childIndexLeft+1;

    int valLeft = 0;
    int valRight = 0;

    if(childIndexLeft <= m_numElements)
    {
        valLeft = GetTreeHeight(childIndexLeft);
    }

    if(childIndexRight <= m_numElements)
    {
        valRight = GetTreeHeight(childIndexRight);
    }

    return std::max(valLeft,valRight)+1;
}

void DoPrintHeap(int index, size_t recursionLevel, int numIndents)
{
    const int value = m_pHeapPointer[index-1];

    if(value == g_TerminationNodeValue)
    {
        return;
    }

    if(m_printVec.size() == recursionLevel)
    {
        m_printVec.push_back(std::string(""));
    }

    const int numLoops = numIndents - (int)m_printVec[recursionLevel].size();
    for(int i=0; i<numLoops; ++i)
    {
        m_printVec[recursionLevel].append(" ");
    }

    m_printVec[recursionLevel].append(std::to_string(value));

    const int childIndexLeft = 2*index;
    const int childIndexRight = childIndexLeft+1;

    const int exponent = m_treeHeight-(recursionLevel+1);
    int twoToPower = 1;
    for(int i=0; i<exponent; ++i)
    {
        twoToPower *= 2;
    }
    const int recursionAdjust = twoToPower-(exponent-1);

    if(childIndexLeft <= m_numElements)
    {
        DoPrintHeap(childIndexLeft, recursionLevel+1, numIndents-recursionAdjust);
    }

    if(childIndexRight <= m_numElements)
    {
        DoPrintHeap(childIndexRight, recursionLevel+1, numIndents+recursionAdjust);
    }
}
};

const int g_heapArraySample_Size = 14;
int g_heapArraySample[g_heapArraySample_Size] = {16,14,10,8,7,9,3,2,4,1,g_TerminationNodeValue,g_TerminationNodeValue,g_TerminationNodeValue,0};

int main()
{
    HeapJ myHeap(g_heapArraySample,g_heapArraySample_Size);
    myHeap.Print();

    return 0;
}

/* output looks like this:

           16

     14          10

  8     7     9     3

2   4 1           0

*/
于 2013-08-17T08:46:57.537 回答
2

这是另一个 C++98 实现,具有tree类似的输出。

样本输出:

PHP
└── is
    ├── minor
    │   └── perpetrated
    │       └── whereas
    │           └── skilled
    │               └── perverted
    │                   └── professionals.
    └── a
        ├── evil
        │   ├── incompetent
        │   │   ├── insidious
        │   │   └── great
        │   └── and
        │       ├── created
        │       │   └── by
        │       │       └── but
        │       └── amateurs
        └── Perl

编码:

void printTree(Node* root)
{
        if (root == NULL)
        {
                return;
        }

        cout << root->val << endl;
        printSubtree(root, "");
        cout << endl;
}

void printSubtree(Node* root, const string& prefix)
{
        if (root == NULL)
        {
                return;
        }

        bool hasLeft = (root->left != NULL);
        bool hasRight = (root->right != NULL);

        if (!hasLeft && !hasRight)
        {
                return;
        }

        cout << prefix;
        cout << ((hasLeft  && hasRight) ? "├── " : "");
        cout << ((!hasLeft && hasRight) ? "└── " : "");

        if (hasRight)
        {
                bool printStrand = (hasLeft && hasRight && (root->right->right != NULL || root->right->left != NULL));
                string newPrefix = prefix + (printStrand ? "│   " : "    ");
                cout << root->right->val << endl;
                printSubtree(root->right, newPrefix);
        }

        if (hasLeft)
        {
                cout << (hasRight ? prefix : "") << "└── " << root->left->val << endl;
                printSubtree(root->left, prefix + "    ");
        }
}
于 2018-06-01T20:59:36.090 回答
1

前言

迟到的答案及其在Java中,但我想将我的添加到记录中,因为我发现了如何相对容易地做到这一点,而且我做到这一点的方式更重要。诀窍是要认识到您真正想要的是不要将任何子树直接打印在您的根/子根节点下(在同一列中)。为什么你可能会问?因为它保证了没有间距问题,没有重叠,左子树和右子树没有碰撞的可能性,即使是超长数字。它会自动调整到节点数据的大小。基本思想是将左子树完全打印到根的左侧,而右子树完全打印到根的右侧。

我如何看待这个问题的类比

一个很好的思考方式是用雨伞,首先想象你在外面拿着一把大伞,你代表和你的伞,它下面的一切都是整棵树。把你的左子树想象成一个矮个子(反正比你矮),拿着一把小伞,在你的大伞下在你的左边。您的右子树由一个相似的人表示,在您的右侧有一把同样较小的雨伞。想象一下,如果矮个子男人的雨伞碰过,他们会生气并互相撞击(糟糕的重叠)。你是,你身边的人是你的子树. 您必须正好在他们的雨伞(子树)中间,才能将两人分开,并确保他们永远不会撞到雨伞。诀窍是递归地想象这一点,其中两个人每个人都有自己的两个较小的人在他们的伞下(子节点),他们需要在他们的伞下分开更小的伞(子子树等)伞(子树),它们充当子根。从根本上说,这就是在打印二叉树、子树重叠时“解决”一般问题所需要的。为此,您只需要考虑如何“打印”或“代表”我的模拟中的男性。

我的实现,它的局限性和潜力

首先,我的代码实现接受的参数多于需要的参数(要打印的 currentNode 和节点级别)的唯一原因是因为在打印时我不能轻易地在控制台中移动一行,所以我必须先映射我的行并打印他们反过来。为此,我制作了一个 lineLevelMap,将树的每一行映射到它的输出(这可能对未来有用,作为一种轻松收集树的每一行并同时打印出来的方法)。

//finds the height of the tree beforehand recursively, left to reader as exercise
int height = TreeHeight(root);
//the map that uses the height of the tree to detemrine how many entries it needs
//each entry maps a line number to the String of the actual line
HashMap<Integer,String> lineLevelMap = new HashMap<>();
//initialize lineLevelMap to have the proper number of lines for our tree 
//printout by starting each line as the empty string
for (int i = 0; i < height + 1; i++) {
    lineLevelMap.put(i,"");
} 

如果我可以让 ANSI 转义码在 java 控制台(windows ugh)中工作,我可以简单地向上打印一行,我会将参数计数减少 2,因为我不需要预先映射行或知道树的深度。无论如何,这是我的代码在树的有序遍历中递归:

public int InOrderPrint(CalcTreeNode currentNode, HashMap<Integer,String> 
    lineLevelMap, int level, int currentIndent){
        //traverse left case
        if(currentNode.getLeftChild() != null){
            //go down one line
            level--;
            currentIndent = 
           InOrderPrint(currentNode.getLeftChild(),lineLevelMap,level,currentIndent);
            //go up one line
            level++;

    }
    //find the string length that already exists for this line
    int previousIndent = lineLevelMap.get(level).length();
    //create currentIndent - previousIndent spaces here
    char[] indent = new char[currentIndent-previousIndent];
    Arrays.fill(indent,' ');
    //actually append the nodeData and the proper indent to add on to the line 
    //correctly
    lineLevelMap.put(level,lineLevelMap.get(level).concat(new String(indent) + 
    currentNode.getData()));
    //update the currentIndent for all lines
    currentIndent += currentNode.getData().length();

    //traverse right case
    if (currentNode.getRightChild() != null){
        //go down one line
        level--;
        currentIndent = 
        InOrderPrint(currentNode.getRightChild(),lineLevelMap,level,currentIndent);
        //go up one line
        level++;
    }
    return currentIndent;
}

要在 Java 中实际将此 Tree 打印到控制台,只需使用我们生成的 LineMap。这样我们就可以将线条正面朝上打印

for (int i = height; i > -1; i--) {
    System.out.println(lineLevelMap.get(i));
}

这一切是如何运作的

InorderPrint 子函数完成所有“工作”,并且可以递归地打印出任何节点及其子树。更好的是,它将它们均匀地隔开,您可以轻松地对其进行修改以均匀地隔开所有节点(只需使 Nodedata 相等或让算法认为它是)。它工作得这么好的原因是因为它使用节点的数据长度来确定下一个缩进应该在哪里。这确保了左子树总是在根和右子树之前打印,因此如果您以递归方式确保这一点,则不会在其根下打印左节点,也不会在其根根下打印,依此类推,对于任何右节点都是如此。相反,根和所有子根直接位于其子树的中间,不会浪费空间。

输入为 3 + 2 的示例输出在控制台中如下所示:

Java 控制台中 3 + 2 的示例

3 + 4 * 5 + 6 的示例是:

3 + 4 * 5 + 6 的示例

最后是 ( 3 + 4 ) * ( 5 + 6 ) 的例子,注意括号是:

( 3 + 4 ) * ( 5 + 6 ) 示例

好的,但为什么是有序的?

中序遍历之所以如此有效,是因为它总是先打印最左边的东西,然后是根,然后是最右边的东西。正是我们希望子树的样子:根左侧的所有内容都打印到根的左侧,右侧的所有内容都打印到右侧。中序遍历自然允许这种关系,并且由于我们基于 nodeData 打印行并进行缩进,因此我们无需担心数据的长度。该节点可能有 20 个字符长,并且不会影响算法(尽管您可能会开始用完实际屏幕空间)。该算法不会在节点之间创建任何间距,但可以轻松实现,重要的是它们不会重叠。

只是为了证明给你看(不要相信我的话)这里是一个带有一些很长字符的例子

在此处输入图像描述

如您所见,它只是根据数据的大小进行调整,没有重叠!只要你的屏幕够大。如果有人想出一种在 java 控制台中打印一条线的简单方法(我全神贯注)这将变得更加简单,对于几乎任何具有树基本知识的人来说都足够容易理解和使用,并且最好的部分是否没有严重重叠错误的风险。

于 2018-10-08T12:55:43.403 回答
0

对于一个数组,我发现这更简洁。只需传入数组即可。可以改进以处理非常大的数字(长数字长度)。复制和粘贴 C++ :)

#include <math.h>
using namespace std;   
void printSpace(int count){
    for (int x = 0; x<count; x++) {
        cout<<"-";
    }
}
void printHeap(int heap[], int size){
    cout<<endl;
    int height = ceil(log(size)+1); //+1 handle the last leaves
    int width = pow(2, height)*height;
    int index = 0;
    for (int x = 0; x <= height; x++) { //for each level of the tree
        for (int z = 0; z < pow(2, x); z++) { // for each node on that tree level
            int digitWidth = 1;
            if(heap[index] != 0) digitWidth = floor(log10(abs(heap[index]))) + 1;
            printSpace(width/(pow(2,x))-digitWidth);
            if(index<size)cout<<heap[index++];
            else cout<<"-";
            printSpace(width/(pow(2,x)));
        }
        cout<<endl;
    }
}
于 2013-09-10T17:58:49.953 回答
0

进行有序遍历,在移动到兄弟姐妹之前下降到孩子。在每个级别,即当您下降到一个孩子时,增加缩进。在输出每个节点后,打印一个换行符。

一些伪代码。Print用你的树根打电话。

void PrintNode(int indent, Node* node)
{
    while (--indent >= 0)
        std::cout << " ";
    std::cout << node->value() << "\n";
}

void PrintNodeChildren(int indent, Node* node)
{
    for (int child = 0; child < node->ChildCount(); ++child)
    {
        Node* childNode = node->GetChild(child);
        PrintNode(indent, childNode);
        PrintNodeChildren(indent + 1, childNode);
    }
}

void Print(Node* root)
{
   int indent = 0;
   PrintNode(indent, root);
   PrintNodeChildren(indent + 1, root);  
}
于 2012-11-21T01:23:55.470 回答
0

这是以紧凑方式打印一般树形图的预购例程:

        void preOrder(Node* nd, bool newLine=false,int indent=0)
        {
                if(nd != NULL) {    
                        if (newLine && indent) {
                                std::cout << "\n" << std::setw(indent) << ' '
                        }  else if(newLine)
                                std::cout << "\n";
                        cout<< nd->_c;
                        vector<Node *> &edges=nd->getEdges();
                        int eSize=edges.size();
                        bool nwLine=false;
                        for(int i=0; i<eSize; i++) {
                                preOrder(edges[i],nwLine,indent+1);
                                nwLine=true;
                        }
                }
        }

int printGraph()
{
     preOrder(root,true);
}
于 2014-03-01T18:38:54.303 回答
0

从你的根源开始,数一数你剩下的孩子的数量。从左孩子的总数中,继续打印带有左孩子数量缩进的根。移动到树的下一层,左孩子的缩进数量减少,然后是右孩子的初始两个缩进。根据其级别和其父级减少左子级的缩进,其右兄弟级具有双缩进。

于 2012-11-21T01:39:52.397 回答
0

这是我的代码。它打印得很好,可能不是完全对称。小说明:

  • 第一个功能 - 逐级打印(根 lv -> 离开 lv)
  • 第二个功能 - 距新行开头的距离
  • 第三个功能 - 打印节点并计算两次打印之间的距离;

void Tree::TREEPRINT()
{
    int i = 0;
    while (i <= treeHeight(getroot())){
        printlv(i);
        i++;
        cout << endl;
    }
}

void Tree::printlv(int n){
    Node* temp = getroot();
    int val = pow(2, treeHeight(root) -n+2);
    cout << setw(val) << "";
    prinlv(temp, n, val);
}

void Tree::dispLV(Node*p, int lv, int d)
{
    int disp = 2 * d;
    if (lv == 0){
        if (p == NULL){

            cout << " x ";
            cout << setw(disp -3) << "";
            return;
        }
        else{
            int result = ((p->key <= 1) ? 1 : log10(p->key) + 1);
            cout << " " << p->key << " ";
            cout << setw(disp - result-2) << "";
        }
    }
    else
    {
        if (p == NULL&& lv >= 1){
            dispLV(NULL, lv - 1, d);
            dispLV(NULL, lv - 1, d);
        }
        else{
            dispLV(p->left, lv - 1, d);
            dispLV(p->right, lv - 1, d);
        }
    }
}   

输入:

50-28-19-30-29-17-42-200-160-170-180-240-44-26-27

输出:https ://i.stack.imgur.com/TtPXY.png

于 2017-07-31T22:34:12.560 回答
0

我有一个更简单的代码............考虑一棵由结构节点组成的树

 struct treeNode{
  treeNode *lc;
  element data;
  short int bf;
  treeNode *rc;
};

树的深度可以使用

int depth(treeNode *p){
    if(p==NULL) return 0;
    int l=depth(p->lc);
    int r=depth(p->rc);
    if(l>=r)
        return l+1;
    else
        return r+1;
}

下面的 gotoxy 函数将您的光标移动到所需的位置

void gotoxy(int x,int y)
{
printf("%c[%d;%df",0x1B,y,x);
}

然后打印一棵树可以这样完成:

void displayTreeUpDown(treeNode * root,int x,int y,int px=0){
if(root==NULL) return;
gotoxy(x,y);
int a=abs(px-x)/2;
cout<<root->data.key;
displayTreeUpDown(root->lc,x-a,y+1,x);
displayTreeUpDown(root->rc,x+a,y+1,x);
}

可以使用以下方法调用:

display(t,pow(2,depth(t)),1,1);
于 2017-03-29T02:13:04.197 回答
0

此代码是用 C 编写的。它基本上会“逐层”打印树。

输出示例:三棵不同树的输出示例

函数 rb_tree_putchar_fd() 可以替换为在屏幕上打印的基本函数,例如std::cout << ... ;

SIZE_LEAF_DEBUG 应替换为 int,并且应为偶数。使用 6 方便。

函数 display() 有一个作用:总是在屏幕上打印 SIZE_LEAF_DEBUG 字符。我在示例中使用了 '[' + 4 个字符 + ']'。例如,这四个字符可以是 int 的字符串表示形式。

//#include "rb_tree.h"
#define SIZE_LEAF_DEBUG 6
int             rb_tree_depth(t_rb_node *root);

/*
** note:    This debugging function will display the red/black tree in a tree
**          fashion.
**          RED nodes are displayed in red.
**
** note:    The custom display func takes care of displaying the item of a node
**          represented as a string of SIZE_LEAF_DEBUG characters maximum,
**          padded with whitespaces if necessary. If item is null: the leaf is
**          represented as "[null]"...
**
** note:    the define SIZE_LEAF_DEBUG should be used by the display func.
**          SIZE_LEAF_DEBUG should be an even number.
**
** note:    Every node is represented by:
**          - either whitespaces if NULL
**          - or between squarred brackets a string representing the item.
*/

/*
**  int max;        //max depth of the rb_tree
**  int current;    //current depth while recursing
**  int bottom;     //current is trying to reach bottom while doing a bfs.
*/

typedef struct  s_depth
{
    int         max;
    int         current;
    int         bottom;
}               t_depth;

static  void    rb_tree_deb2(t_rb_node *node, t_depth depth, void (*display)())
{
    int size_line;
    int i;

    i = 0;
    size_line = (1 << (depth.max - ++depth.current)) * SIZE_LEAF_DEBUG;
    if (!node)
    {
        while (i++ < size_line)
            rb_tree_putchar_fd(' ', 1);
        return ;
    }
    if (depth.current == depth.bottom)
    {
        while (i++ < (size_line - SIZE_LEAF_DEBUG) / 2)
            rb_tree_putchar_fd(' ', 1);
        if (node->color == RB_RED)
            rb_tree_putstr_fd("\033[31m", 1);
        display(node->item);
        rb_tree_putstr_fd("\033[0m", 1);
        while (i++ <= (size_line - SIZE_LEAF_DEBUG))
            rb_tree_putchar_fd(' ', 1);
        return ;
    }
    rb_tree_deb2(node->left, depth, display);
    rb_tree_deb2(node->right, depth, display);
}

void    rb_tree_debug(t_rb_node *root, void (*display)())
{
    t_depth depths;

    rb_tree_putstr_fd("\n===================================================="\
            "===========================\n====================== BTREE DEBUG "\
            "START ======================================\n", 1);
    if (root && display)
    {
        depths.max = rb_tree_depth((t_rb_node*)root);
        depths.current = 0;
        depths.bottom = 0;
        while (++depths.bottom <= depths.max)
        {
            rb_tree_deb2(root, depths, display);
            rb_tree_putchar_fd('\n', 1);
        }
    }
    else
        rb_tree_putstr_fd("NULL ROOT, or NULL display func\n", 1);
    rb_tree_putstr_fd("\n============================== DEBUG END ==========="\
            "===========================\n==================================="\
            "============================================\n\n\n", 1);
}
于 2021-06-08T23:28:59.247 回答