Print a Binary Tree in a Pretty Way

Print binary tree in a pretty way using c++

Here is an example of code creating a text-based representation of a binary tree. This demonstration uses a minimally useful binary tree class (BinTree), with a small footprint, just to avoid bloating the example's size.

Its text-rendering member functions are more serious, using iteration rather than recursion, as found in other parts of the class.

This does its job in three steps, first a vector of rows of string values is put together.

Then this is used to format lines of text strings representing the tree.

Then the strings are cleaned up and dumped to cout.

As an added bonus, the demo includes a "random tree" feature, for hours of nonstop entertainment.

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <random>

using std::vector;
using std::string;
using std::cout;

template <typename T>
class BinTree {
struct Node {
T value;
Node *left,*right;
Node() : left(nullptr),right(nullptr) {}
Node(const T& value) :value(value),left(nullptr),right(nullptr) {}
// stack-abusing recursion everywhere, for small code
~Node() { delete left; delete right; }
int max_depth() const {
const int left_depth = left ? left->max_depth() : 0;
const int right_depth = right ? right->max_depth() : 0;
return (left_depth > right_depth ? left_depth : right_depth) + 1;
}
};

Node *root;

public:
BinTree() : root(nullptr) {}
~BinTree() { delete root; }

int get_max_depth() const { return root ? root->max_depth() : 0; }
void clear() { delete root; root = nullptr; }
void insert() {}
template <typename ...Args>
void insert(const T& value, Args...more) {
if(!root) {
root = new Node(value);
} else {
Node* p = root;
for(;;) {
if(value == p->value) return;
Node* &pchild = value < p->value ? p->left : p->right;
if(!pchild) {
pchild = new Node(value);
break;
}
p = pchild;
}
}
insert(more...);
}

struct cell_display {
string valstr;
bool present;
cell_display() : present(false) {}
cell_display(std::string valstr) : valstr(valstr), present(true) {}
};

using display_rows = vector< vector< cell_display > >;

// The text tree generation code below is all iterative, to avoid stack faults.

// get_row_display builds a vector of vectors of cell_display structs
// each vector of cell_display structs represents one row, starting at the root
display_rows get_row_display() const {
// start off by traversing the tree to
// build a vector of vectors of Node pointers
vector<Node*> traversal_stack;
vector< std::vector<Node*> > rows;
if(!root) return display_rows();

Node *p = root;
const int max_depth = root->max_depth();
rows.resize(max_depth);
int depth = 0;
for(;;) {
// Max-depth Nodes are always a leaf or null
// This special case blocks deeper traversal
if(depth == max_depth-1) {
rows[depth].push_back(p);
if(depth == 0) break;
--depth;
continue;
}

// First visit to node? Go to left child.
if(traversal_stack.size() == depth) {
rows[depth].push_back(p);
traversal_stack.push_back(p);
if(p) p = p->left;
++depth;
continue;
}

// Odd child count? Go to right child.
if(rows[depth+1].size() % 2) {
p = traversal_stack.back();
if(p) p = p->right;
++depth;
continue;
}

// Time to leave if we get here

// Exit loop if this is the root
if(depth == 0) break;

traversal_stack.pop_back();
p = traversal_stack.back();
--depth;
}

// Use rows of Node pointers to populate rows of cell_display structs.
// All possible slots in the tree get a cell_display struct,
// so if there is no actual Node at a struct's location,
// its boolean "present" field is set to false.
// The struct also contains a string representation of
// its Node's value, created using a std::stringstream object.
display_rows rows_disp;
std::stringstream ss;
for(const auto& row : rows) {
rows_disp.emplace_back();
for(Node* pn : row) {
if(pn) {
ss << pn->value;
rows_disp.back().push_back(cell_display(ss.str()));
ss = std::stringstream();
} else {
rows_disp.back().push_back(cell_display());
} } }
return rows_disp;
}

// row_formatter takes the vector of rows of cell_display structs
// generated by get_row_display and formats it into a test representation
// as a vector of strings
vector<string> row_formatter(const display_rows& rows_disp) const {
using s_t = string::size_type;

// First find the maximum value string length and put it in cell_width
s_t cell_width = 0;
for(const auto& row_disp : rows_disp) {
for(const auto& cd : row_disp) {
if(cd.present && cd.valstr.length() > cell_width) {
cell_width = cd.valstr.length();
} } }

// make sure the cell_width is an odd number
if(cell_width % 2 == 0) ++cell_width;

// allows leaf nodes to be connected when they are
// all with size of a single character
if(cell_width < 3) cell_width = 3;


// formatted_rows will hold the results
vector<string> formatted_rows;

// some of these counting variables are related,
// so its should be possible to eliminate some of them.
s_t row_count = rows_disp.size();

// this row's element count, a power of two
s_t row_elem_count = 1 << (row_count-1);

// left_pad holds the number of space charactes at the beginning of the bottom row
s_t left_pad = 0;

// Work from the level of maximum depth, up to the root
// ("formatted_rows" will need to be reversed when done)
for(s_t r=0; r<row_count; ++r) {
const auto& cd_row = rows_disp[row_count-r-1]; // r reverse-indexes the row
// "space" will be the number of rows of slashes needed to get
// from this row to the next. It is also used to determine other
// text offsets.
s_t space = (s_t(1) << r) * (cell_width + 1) / 2 - 1;
// "row" holds the line of text currently being assembled
string row;
// iterate over each element in this row
for(s_t c=0; c<row_elem_count; ++c) {
// add padding, more when this is not the leftmost element
row += string(c ? left_pad*2+1 : left_pad, ' ');
if(cd_row[c].present) {
// This position corresponds to an existing Node
const string& valstr = cd_row[c].valstr;
// Try to pad the left and right sides of the value string
// with the same number of spaces. If padding requires an
// odd number of spaces, right-sided children get the longer
// padding on the right side, while left-sided children
// get it on the left side.
s_t long_padding = cell_width - valstr.length();
s_t short_padding = long_padding / 2;
long_padding -= short_padding;
row += string(c%2 ? short_padding : long_padding, ' ');
row += valstr;
row += string(c%2 ? long_padding : short_padding, ' ');
} else {
// This position is empty, Nodeless...
row += string(cell_width, ' ');
}
}
// A row of spaced-apart value strings is ready, add it to the result vector
formatted_rows.push_back(row);

// The root has been added, so this loop is finsished
if(row_elem_count == 1) break;

// Add rows of forward- and back- slash characters, spaced apart
// to "connect" two rows' Node value strings.
// The "space" variable counts the number of rows needed here.
s_t left_space = space + 1;
s_t right_space = space - 1;
for(s_t sr=0; sr<space; ++sr) {
string row;
for(s_t c=0; c<row_elem_count; ++c) {
if(c % 2 == 0) {
row += string(c ? left_space*2 + 1 : left_space, ' ');
row += cd_row[c].present ? '/' : ' ';
row += string(right_space + 1, ' ');
} else {
row += string(right_space, ' ');
row += cd_row[c].present ? '\\' : ' ';
}
}
formatted_rows.push_back(row);
++left_space;
--right_space;
}
left_pad += space + 1;
row_elem_count /= 2;
}

// Reverse the result, placing the root node at the beginning (top)
std::reverse(formatted_rows.begin(), formatted_rows.end());

return formatted_rows;
}

// Trims an equal number of space characters from
// the beginning of each string in the vector.
// At least one string in the vector will end up beginning
// with no space characters.
static void trim_rows_left(vector<string>& rows) {
if(!rows.size()) return;
auto min_space = rows.front().length();
for(const auto& row : rows) {
auto i = row.find_first_not_of(' ');
if(i==string::npos) i = row.length();
if(i == 0) return;
if(i < min_space) min_space = i;
}
for(auto& row : rows) {
row.erase(0, min_space);
} }

// Dumps a representation of the tree to cout
void Dump() const {
const int d = get_max_depth();

// If this tree is empty, tell someone
if(d == 0) {
cout << " <empty tree>\n";
return;
}

// This tree is not empty, so get a list of node values...
const auto rows_disp = get_row_display();
// then format these into a text representation...
auto formatted_rows = row_formatter(rows_disp);
// then trim excess space characters from the left sides of the text...
trim_rows_left(formatted_rows);
// then dump the text to cout.
for(const auto& row : formatted_rows) {
std::cout << ' ' << row << '\n';
}
}
};


int main() {
BinTree<int> bt;

// Build OP's tree
bt.insert(8,5,2,6,10,9,11);
cout << "Tree from OP:\n\n";
bt.Dump();
cout << "\n\n";

bt.clear();

// Build a random tree
// This toy tree can't balance, so random
// trees often look more like linked lists.
// Just keep trying until a nice one shows up.
std::random_device rd;
std::mt19937 rng(rd());

int MaxCount=20;
int MaxDepth=5;
const int Min=0, Max=1000;

std::uniform_int_distribution<int> dist(Min,Max);

while(MaxCount--) {
bt.insert(dist(rng));
if(bt.get_max_depth() >= MaxDepth) break;
}

cout << "Randomly generated tree:\n\n";
bt.Dump();
}

An example of the output:

Tree from OP:

8
/ \
/ \
/ \
5 10
/ \ / \
2 6 9 11


Randomly generated tree:

703
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
137 965
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
41 387 786
\ / \ / \
\ / \ / \
\ / \ / \
95 382 630 726 813
\
841

Print a binary tree in a pretty way

In order to pretty-print a tree recursively, you need to pass two arguments to your printing function:

  • The tree node to be printed, and
  • The indentation level

For example, you can do this:

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 ";
}
}

The initial call should be postorder(root);

If you would like to print the tree with the root at the top, move cout to the top of the if.

How to print binary tree diagram in Java?

I've created simple binary tree printer. You can use and modify it as you want, but it's not optimized anyway. I think that a lot of things can be improved here ;)

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;
}

}

Output 1 :

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

Output 2 :

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

How to print binary search tree in nice diagram?

You could use these functions. They return a string, so it is up to the caller to print it.

I also find it nicer when the right subtree is printed upwards, and the left subtree downwards. That way, the tree is just rotated 90° from how it is usually depicted -- with the root at the top.

Here is the relevant code:

    public String pretty() {
return pretty(root, "", 1);
}

private String pretty(Node root, String prefix, int dir) {
if (root == null) {
return "";
}

String space = " ".repeat(("" + root.data).length());
return pretty(root.right, prefix + "│ ".charAt(dir) + space, 2)
+ prefix + "└ ┌".charAt(dir) + root.data
+ " ┘┐┤".charAt((root.left != null ? 2 : 0)
+ (root.right != null ? 1 : 0)) + "\n"
+ pretty(root.left, prefix + " │".charAt(dir) + space, 0);
}

print binary tree level by level in python

What you're looking for is breadth-first traversal, which lets you traverse a tree level by level. Basically, you use a queue to keep track of the nodes you need to visit, adding children to the back of the queue as you go (as opposed to adding them to the front of a stack). Get that working first.

After you do that, then you can figure out how many levels the tree has (log2(node_count) + 1) and use that to estimate whitespace. If you want to get the whitespace exactly right, you can use other data structures to keep track of how many spaces you need per level. A smart estimation using number of nodes and levels should be enough, though.

Print Simple Binary Search Tree in C

you can do a Tree traversal using the Pre-order technique

Pre-order (NLR)

  1. Access the data part of the current node.

  2. Traverse the left subtree by recursively calling the pre-order function.

  3. Traverse the right subtree by recursively calling the pre-order function.

keeping count of the current level to print the indentation. for example:

#include <stdio.h>

struct node_struct
{
int data;
struct node_struct *right, *left;
};
typedef struct node_struct Node;

void treeprint(Node *root, int level)
{
if (root == NULL)
return;
for (int i = 0; i < level; i++)
printf(i == level - 1 ? "|-" : " ");
printf("%d\n", root->data);
treeprint(root->left, level + 1);
treeprint(root->right, level + 1);
}

int main(void)
{
Node a, b, c, d, e, f;

a.data = 6;
a.left = &b;
a.right = &c;

b.data = 2;
b.left = &d;
b.right = &e;

c.data = 9;
c.left = NULL;
c.right = NULL;

d.data = 1;
d.left = &f;
d.right = NULL;

e.data = 4;
e.left = NULL;
e.right = NULL;

f.data = 8;
f.left = NULL;
f.right = NULL;

treeprint(&a, 0);
}

$ cc tree.c
$ ./a.out
6
|-2
|-1
|-8
|-4
|-9
$

Best way to print a binary-tree in one line?

just add end="" in your print()

def walk(self,x):
if x!=None:
self.walk(x.getLeft())
print(x.getData(), end="")
self.walk(x.getRight())

Print Binary Tree - C++ [duplicate]

Consider the following example

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

// .... your code ....

void buildTree(AVLNode* root, int scrWidth, int itemWidth)
// breadth-first traversal with depth limit based on screen width and output field width for one elemet
{
bool notFinished = false;
// check the root
if (root)
{
notFinished = true;
}
// calculate maximum possible depth
int depth = 1;
int field = scrWidth;
while (field > itemWidth)
{
depth++;
field /= 2;
}
// check result
if (depth < 1)
{
cout << " -= erroneous output options =-" << endl;
return;
}
AVLNode** pItems = new AVLNode*[1];
*pItems = root; // pointer to item on the first level
int itemCnt = 1;
int divWidth = 1;
// loop for output not more than depth levels until the data is not finished
// where level is current depth of tree, and root is on the first level
for (int level = 1; level <= depth && notFinished; level++)
{
itemCnt = (level == 1) ? 1 : (itemCnt * 2);
divWidth *= 2;
// make list of pointers to refer items on next level
AVLNode** list = new AVLNode*[itemCnt * 2];
// output all utems of that level
int nextCnt = 0;
notFinished = false;
for (int i = 0; i < itemCnt; i++, nextCnt += 2)
{
int curWidth = (scrWidth / divWidth) * ((i > 0) ? 2 : 1);
cout << setw((curWidth>=itemWidth) ? curWidth:(itemWidth/(1+(i==0))));
if (pItems[i])
{
cout << pItems[i]->data;
list[nextCnt] = pItems[i]->left;
list[nextCnt + 1] = pItems[i]->right;
if (list[nextCnt] || list[nextCnt + 1])
notFinished = true;
}
else
{
cout << ".";
list[nextCnt] = NULL;
list[nextCnt + 1] = NULL;
}
}
cout << endl;
// free the memory allocated for list of pointers
if (pItems)
delete[] pItems;
pItems = list; // and shift to new one (for next level)
}
delete[] pItems;
}

int main(int argc, char* argv[])
{
// create some structure
AVLNode * root = NULL;
// code for making tree
// ....
buildTree(root, 80, 5);
// some other code
// ....
return 0;
}

Call buildTree(root, 80, 5); prints the tree like (where . means NULL instead of item):

                                     64
58 .
24 62 . .
0 78 . . . . . .
41 69 . . . . . . . . . . . . . .

but buildTree(root, 40, 10); for the same data will output

               64
58 .
24 62 . .

i.e. only three layers, because 4th level has 8 items and if each require 10 character total weight 40 is not enough.

Note: I have not enough time to debug the code and make it perfect, but I hope it will help you to find your own solution.



Related Topics



Leave a reply



Submit