Just wondering if I can get some tips on printing a pretty binary tree in the form of:
5
10
11
7
6
3
4
Here's yet another C++98 implementation, with tree like output.
Sample output:
PHP
└── is
├── minor
│ └── perpetrated
│ └── whereas
│ └── skilled
│ └── perverted
│ └── professionals.
└── a
├── evil
│ ├── incompetent
│ │ ├── insidious
│ │ └── great
│ └── and
│ ├── created
│ │ └── by
│ │ └── but
│ └── amateurs
└── Perl
The code:
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 + " ");
}
}
Do an in-order traversal, descending to children before moving to siblings. At each level, that is when you descent to a child, increase the indent. After each node you output, print a newline.
Some psuedocode. Call Print
with the root of your tree.
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);
}
i have a easier code.......... consider a tree made of nodes of structure
struct treeNode{
treeNode *lc;
element data;
short int bf;
treeNode *rc;
};
Tree's depth can be found out using
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;
}
below gotoxy function moves your cursor to the desired position
void gotoxy(int x,int y)
{
printf("%c[%d;%df",0x1B,y,x);
}
Then Printing a Tree can be done as:
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);
}
which can be called using:
display(t,pow(2,depth(t)),1,1);
It's never going to be pretty enough, unless one does some backtracking to re-calibrate the display output. But one can emit pretty enough binary trees efficiently using heuristics: Given the height of a tree, one can guess what the expected width and setw of nodes at different depths. There are a few pieces needed to do this, so let's start with the higher level functions first to provide context.
The pretty print function:
// 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);
}
}
The above code is easy. The main logic is in the printRow function. Let's delve into that.
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() does what you'd expect: it stores all nodes with a given equal depth into vec. Here's the code for that:
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);
}
Now back to printRow(). For each line, we set the stream width based on how deep we are in the binary tree. This formatting will be nice because, typically, the deeper you go, the more width is needed. I say typically because in degenerate trees, this wouldn't look as pretty. As long as the tree is roughly balanced and smallish (< 20 items), it should turn out fine.
A placeholder is needed to align the '/' and '\' characters properly. So when a row is obtained via getLine(), we insert the placeholder if there isn't any node present at the specified depth. The placeholder can be set to anything like (1<<31)
for example. Obviously, this isn't robust because the placeholder could be a valid node value. If a coder's got spunk and is only dealing with decimals, one could modify the code to emit decimal-converted strings via getLine() and use a placeholder like "_". (Unfortunately, I'm not such a coder :P)
The result for the following items inserted in order: 8, 12, 4, 2, 5, 15 is
8
/ \
4 12
/ \ \
2 5 15
getHeight() is left to the reader as an exercise. :) One could even get prettier results by retroactively updating the setw of shallow nodes based on the number of items in deeper nodes. That too is left to the reader as an exercise.
//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));
}
If your only need is to visualize your tree, a better method would be to output it into a dot format and draw it with grapviz.
You can look at dot guide for more information abt syntax etc