Related
I want to make a Fibonacci tree but with a minimum number other than 1, and I can't seem to find anything about it.
Here's an example of a "normal" fibonacci tree, with the minimum node 1.
5
/ \
3 7
/ \ /
2 4 6
/
1
What I want to do is for example, with minimum 3:
With height 0: it would be the empty tree.
With height 1:
3
With height 2:
4
/
3
With height 3:
5
/ \
4 6
/
3
With height 4:
7
/ \
5 9
/ \ /
4 6 8
/
3
...And so on.
My problem is, I can't seem to see a pattern in it, so I can't think of an algorithm to write.
I know that the left subtree's height is h-1 (where h is the original given height) and the right subtree's height is h-2. And I can't see how they calculate the root's number. But other than that, I'm really stuck.
Since the Fibonacci tree is a recursively defined structure, the easiest way is to think about a recursive algorithm.
This is some sort of C-style pseudo code (not covering any edge cases - I leave that to you as part of the exercise).
function createTree(height)
{
// basic cases
if(height == 0) return NULL;
if(height == 1)
{
node = new Node;
node.numNodesInTree = 1;
}
else
{
// according to the definition of the fibonacci tree
node = new Node;
node.leftChild = createTree(height - 1);
node.rightChild = createTree(height - 2);
node.numNodesInTree = node.leftChild.numNodesInTree
+ node.rightChild.numNodesInTree
+ 1; // also count the current node
}
return node;
}
You end up with a tree that has the Fibonacci structure, but not the right numbers yet. As a little helper, you have the number of nodes for each sub tree.
Then you can do something like:
function fillTree(offset, node, minimum) // offset means "all numbers in this subtree must be bigger than offset"
{
// According to the binary search tree definition,
// all numbers in the left sub tree have to be lower than
// the current node.
// All nodes in the right sub tree have to be larger.
node.value = node.leftChild.numNodesInTree // the number has to be bigger than all numbers in the left sub tree
+ 1 // (that's the "one bigger")
+ offset // offset for right subtrees
+ minimum - 1; // Just stupidly add the minimum (as mentioned in the comment to your question)
fillTree(offset, node.leftChild, minimum); // propagate offset to left children
fillTree(node.value, node.rightChild, minimum); // for the right sub tree, the current node's value is the new offset
// because all nodes in the right sub tree have to be bigger than the current node (binary search criterion)
}
Then you can call it like:
root = createTree(height);
fillTree(0, root, 3); // when initially calling it, the offset is always 0
// You can set the minimum arbitrarily (i.e. 3 as in your example)
Since this is pseudo code, I obviously haven't tested it, but you could get the idea behind that.
A friend of mine was asked this question in an interview.
Given two binary trees, explain how would you create a diff such that if you have that diff and either of the trees you should be able to generate the other binary tree. Implement a function createDiff(Node tree1, Node tree 2) returns that diff.
Tree 1
4
/ \
3 2
/ \ / \
5 8 10 22
Tree 2
1
\
4
/ \
11 12
If you are given Tree 2 and the diff you should be able to generate Tree 1.
My solution:
Convert both the binary trees into array where left child is at 2n+1 and right child is at 2n+2and represent empty node by -1. Then just do element-wise subtraction of the array to create the diff. This solution will fail if tree has -1 as node value and I think there has to be a better and neat solution but I'm not able to figure it out.
Think of them as direcory tres and print a sorted list of the path to every leaf item
Tree 1 becomes:
4/2/10
4/2/22
4/3/5
4/3/8
These list formats can be diff'ed and the tree recreated from such a list.
There are many ways to do this.
I would suggest that you turn the tree into a sorted array of triples of (parent, child, direction). So start with tree1:
4
/ \
3 2
/ \ / \
5 8 10 22
This quickly becomes:
(None, 4, None) # top
(4, 3, L)
(3, 5, L)
(3, 8, L)
(4, 2, R)
(2, 10, L)
(2, 22, R)
Which you sort to get
(None, 4, None) # top
(2, 10, L)
(2, 22, R)
(3, 5, L)
(3, 8, L)
(4, 2, R)
(4, 3, L)
Do the same with the other, and then diff them.
Given a tree and the diff, you can first turn the tree into this form, look at the diff, realize which direction it is and get the desired representation with patch. You can then reconstruct the other tree recursively.
The reason why I would do it with this representation is that if the two trees share any subtrees in common - even if they are placed differently in the main tree - those will show up in common. And therefore you are likely to get relatively small diffs if the trees do, in fact, match in some interesting way.
Edit
Per point from #ruakh, this does assume that values do not repeat in a tree. If they do, then you could do a representation like this:
4
/ \
3 2
/ \ / \
5 8 10 22
becomes
(, 4)
(0, 3)
(00, 5)
(01, 8)
(1, 2)
(10, 10)
(11, 22)
And now if you move subtrees, they will show up as large diffs. But if you just change one node, it will still be a small diff.
(The example from the question(/interview) is not very helpful in not showing any shared sub-structure of non-trivial size. Or the interview question outstanding for initiating a dialogue between customer and developer.)
Re-use of subtrees needs a representation allowing to identify such. It seems useful to be able to reconstruct the smaller tree without walking most of the difference. Denoting "definition" of identifiable sub-trees with capital letters and re-use by a tacked-on ':
d e d--------e
c b "-" c b => C B' C' b
b a a b a a B a a
a a a
(The problem statement does not say diff is linear.)
Things to note:
there's a sub-tree B occurring in two places of T1
in T2, there's another b with one leaf-child a that is not another occurrence of B
no attempt to share leaves
What if now I imagine (or the interviewer suggests) two huge trees, identical but for one node somewhere in the middle which has a different value?
Well, at least its sub-trees will be shared, and "the other sub-trees" all the way up to the root. Too bad if the trees are degenerated and almost all nodes are part of that path.
Huge trees with children of the root exchanged?
(Detecting trees occurring more than once has a chance to shine here.)
The bigger problem would seem to be the whole trees represented in "the diff", while the requirement may be
Given one tree, the diff shall support reconstruction of the other using little space and processing.
(It might include setting up the diff shall be cheap, too - which I'd immediately challenge: small diff looks related to editing distance.)
A way to identify "crucial nodes" in each tree is needed - btilly's suggestion of "left-right-string" is good as gold.
Then, one would need a way to keep differences in children & value.
That's the far end I'd expect an exchange in an interview to reach.
To detect re-used trees, I'd add the height to each internal node. For a proof of principle, I'd probably use an existing implementation of find repeated strings on a suitable serialisation.
There are many ways to think of a workable diff-structure.
Naive solution
One naive way is to store the two trees in a tuple. Then, when you need to regenerate a tree, given the other and the diff, you just look for a node that is different when comparing the given tree with the tree in the first tuple entry of the diff. If found you return that tree from the first tuple entry. If not found, you return the second one from the diff tuple.
Small diffs for small differences
An interviewer would probably ask for a less memory consuming alternative. One could try to think of a structure that will be small in size when there are only a few values or nodes different. In the extreme case where both trees are equal, such diff would be (near-)empty as well.
Definitions
I define these terms before defining the diff's structure:
Imagine the trees get extra NIL leaf nodes, i.e. an empty tree would consist of 1 NIL node. A tree with only a root node, would have two NIL nodes as its direct children, ...etc.
A node is common to both trees when it can be reached via the same path from the root (e.g. left-left-right), irrespective of whether they contain the same value or have the same children. A node can even be common when it is a NIL node in one or both of the trees (as defined above).
Common nodes (including NIL nodes when they are common) get a preorder sequence number (0, 1, 2, ...). Nodes that are not common are discarded during this numbering.
Diff structure
The difference could be a list of tuples, where each tuple has this information:
The above mentioned preorder sequence number, identifying a common node
A value: when neither nodes is a NIL node, this is the diff of the values (e.g. XOR). When one of the nodes is a NIL node, the value is the other node object (so effectively including the whole subtree below it). In typeless languages, either information can fit in the same tuple position. In strongly typed languages, you would use an extra entry in the tuple (e.g. atomicValue, subtree), where only one of two would have a significant value.
A tuple will only be added for a common node, and only when either their values differ, and at least one of both is a not-NIL node.
Algorithm
The diff can be created via a preorder walk through the common nodes of the trees.
Here is an implementation in JavaScript:
class Node {
constructor(value, left, right) {
this.value = value;
if (left) this.left = left;
if (right) this.right = right;
}
clone() {
return new Node(this.value, this.left ? this.left.clone() : undefined,
this.right ? this.right.clone() : undefined);
}
}
// Main functions:
function createDiff(tree1, tree2) {
let i = -1; // preorder sequence number
function recur(node1, node2) {
i++;
if (!node1 !== !node2) return [[i, (node1 || node2).clone()]];
if (!node1) return [];
const result = [];
if (node1.value !== node2.value) result.push([i, node1.value ^ node2.value]);
return result.concat(recur(node1.left, node2.left), recur(node1.right, node2.right));
}
return recur(tree1, tree2);
}
function applyDiff(tree, diff) {
let i = -1; // preorder sequence number
let j = 0; // index in diff array
function recur(node) {
i++;
let diffData = j >= diff.length || diff[j][0] !== i ? 0 : diff[j++][1];
if (diffData instanceof Node) return node ? undefined : diffData.clone();
return node && new Node(node.value ^ diffData, recur(node.left), recur(node.right));
}
return recur(tree);
}
// Create sample data:
let tree1 =
new Node(4,
new Node(3,
new Node(5), new Node(8)
),
new Node(2,
new Node(10), new Node(22)
)
);
let tree2 =
new Node(2,
undefined,
new Node(4,
new Node(11), new Node(12)
)
);
// Demo:
let diff = createDiff(tree1, tree2);
console.log("Diff:");
console.log(diff);
const restoreTree2 = applyDiff(tree1, diff);
console.log("Is restored second tree equal to original?");
console.log(JSON.stringify(tree2)===JSON.stringify(restoreTree2));
const restoreTree1 = applyDiff(tree2, diff);
console.log("Is restored first tree equal to original?");
console.log(JSON.stringify(tree1)===JSON.stringify(restoreTree1));
const noDiff = createDiff(tree1, tree1);
console.log("Diff for two equal trees:");
console.log(noDiff);
Given a binary tree that might contain duplicate values, you need to find minimum distance between two given values. Note that the binary tree can be large.
For example:
5
/ \
1 7
/ \ / \
4 3 8 2
/ \
1 2
The function should return 2 for (1 and 2 as input).
(If duplicates are not present, we can find LCA and then calculate the distance.)
I've written the following code but I couldn't handle cases when the values are present in different subtrees and in the below cases:
root = 1, root.left = 4, root.left.left = 3, root.left.right = 2, root.left.left.left = 1
root = 1, root.left = 4, root.left.left = 3, root.left.left.left = 1, root.left.left.right = 2
void dist(struct node* root,int& min,int n1,int n2,int pos1,int pos2,int level) {
if(!root)
return;
if(root->data==n1){
pos1 = level;
if(pos2>=0)
if(pos1-pos2 < min)
min = pos1-pos2;
}
else if(root->data==n2){
pos2 = level;
if(pos1>=0)
if(pos2-pos1 < min)
min = pos2-pos1;
}
dist(root->left,min,n1,n2,pos1,pos2,level+1);
dist(root->right,min,n1,n2,pos1,pos2,level+1);
}
I think at each node we can find if that node is the LCA of the values or not. If that node is LCA then find the distance and update min accordingly, but this would take O(n2).
Following is an algorithm to solve the problem:-
traverse all of the tree and calculate paths for each node using binary strings representation and store into hash map
eg. For your tree the hashmap will be
1 => 0,000
2 => 001,11
3 => 01
...
When query for distance between (u,v) check for each pair and calculate distance between them. Remove common prefix from strings and then sum the remaining lengths
eg. u=1 and v=2
distance(0,001) = 2
distance(0,11) = 3
distance(000,001) = 2
distance(000,11) = 5
min = 2
Note: I think the second step can be made more efficient but need to do more research
You can compute the LCA of a set of nodes by computing LCA(x1, LCA(x2, LCA(x3... and all the nodes in the set will be somewhere below this LCA. If you compare the LCAs of two sets of nodes and one is not directly beneath the other then the minimum distance between any two nodes in different sets will be at least the distance between the LCAs. If one LCA is above the other then the minimum distance could be zero.
This allows a sort of branch and bound approach. At each point you have a best minimum distance so far (initialized as infinity). Given two sets of nodes, use their LCAs to work out a lower bound on their minimum distance and discard them if this is no better than the best answer so far. If not discarded, split each set into two plus a possible single depending on whether each node in the set is to the left of the LCA, to the right of the LCA, or is the LCA. Recursively check for the minimum distance in the (up to nine) pairs of split sets. If both splits in a pair are below some minimum size, just work out the LCAs and minimum distances of each pair of nodes across the two sets - at this point may find out that you have a new best answer and can update the best answer so far.
Looking at the example at the top of the question, the LCA of the 2s is the root of the tree, and the LCA of the 1s is the highest 1. So the minimum distance between these two sets could be close to zero. Now split each set in two. The left hand 2 is distance two from both of the two 1s. The LCA of the right hand 2 is itself, on the right hand branch of the tree, and the LCA of each of the two 1s is down on the left hand branch of the tree. So the distance between the two is at least two, and we could tell this even if we had a large number of 2s anywhere below the position of the existing right-hand two, and a large number of 1s anywhere on the left hand subtree.
Do a pre-order traversal of the tree (or any traversal should work).
During this process, simply keep track of the closest 1 and 2, and update the distance whenever you find a 2 and the closest 1 is closer than the closest distance so far, or vice versa.
Code (C++, untested first draft): (hardcoded 1 and 2 for simplicity)
int getLeastDistance(Node *n, int *distTo1, int *distTo2)
{
if (n == NULL)
return;
int dist = LARGE_VALUE;
// process current node
if (n->data == 1)
{
dist = *distTo2;
*distTo1 = 0;
}
else if (n->data == 2)
{
dist = *distTo1;
*distTo2 = 0;
}
// go left
int newDistTo1 = *distTo1 + 1,
newDistTo2 = *distTo2 + 1;
dist = min(dist, getLeastDistance(n->left, &newDistTo1, &newDistTo2));
// update distances
*distTo1 = min(*distTo1, newDistTo1 + 1);
*distTo2 = min(*distTo2, newDistTo2 + 1);
// go right
newDistTo1 = *distTo1 + 1;
newDistTo2 = *distTo2 + 1;
dist = min(dist, getLeastDistance(n->right, &newDistTo1, &newDistTo2));
}
Caller:
Node root = ...;
int distTo1 = LARGE_VALUE, distTo2 = LARGE_VALUE;
int dist = getLeastDistance(&root, &distTo1, &distTo2);
Just be sure to make LARGE_VALUE far enough from the maximum value for int such that it won't overflow if incremented (-1 is probably safer, but it requires more complex code).
Given a binary tree which is huge and can not be placed in memory, how do you check if the tree is a mirror image.
I got this as an interview question
If a tree is a mirror image of another tree, the inorder traversal of one tree would be reverse of another.
So just do inorder traversal on the first tree and a reverse inorder traversal on another and check if all the elements are the same.
I can't take full credit for this reply of course; a handful of my colleagues helped with some assumptions and for poking holes in my original idea. Much thanks to them!
Assumptions
We can't have the entire tree in memory, so it's not ideal to use recursion. Let's assume, for simplicity's sake, that we can only hold a maximum of two nodes in memory.
We know n, the total number of levels in our tree.
We can perform seeks on the data with respect to the character or line position it's in.
The data that is on disk is ordered by depth. That is to say, the first entry on disk is the root, and the next two are its children, and the next four are its children's children, and so forth.
There are cases in which the data is perfectly mirrored, and cases in which it isn't. Blank data interlaced with non-blank data is considered "acceptable", unless otherwise specified.
We have freedom over using any data type we wish so long as the values can be compared for equivalence. Testing for object equivalence may not be ideal, so let's assume we're comparing primitives.
"Mirrored" means mirrored between the root's children. To use different terminologies, the grandparent's left child is mirrored with its right child, and the left child (parent)'s left child is mirrored with the grandparent's right child's right child. This is illustrated in the graph below; the matching symbols represent the mirroring we want to check for.
G
P* P*
C1& C2^ C3^ C4&
Approach
We know how many nodes on each level we should expect when we're reading from disk - some multiple of 2k. We can establish a double loop to iterate over the total depth of the tree, and the count of the nodes in each level. Inside of this, we can simply compare the outermost values for equivalence, and short-circuit if we find an unequal value.
We can determine the location of each outer location by using multiples of 2k. The leftmost child of any level will always be 2k, and the rightmost child of any level will always be 2k+1-1.
Small Proof: Outermost nodes on level 1 are 2 and 3; 21 = 2, 21+1-1 = 22-1 = 3. Outermost nodes on level 2 are 4 and 7; 22 = 4, 22+1-1 = 23-1 = 7. One could expand this all the way to the nth case.
Pseudocode
int k, i;
for(k = 1; k < n; k++) { // Skip root, trivially mirrored
for(i = 0; i < pow(2, k) / 2; i++) {
if(node_retrieve(i + pow(2, k)) != node_retrieve(pow(2, (k+1)-i)) {
return false;
}
}
}
return true;
Thoughts
This sort of question is a great interview question because, more than likely, they want to see how you would approach this problem. This approach may be horrible, it may be immaculate, but an employer would want you to take your time, draw things on a piece of paper or whiteboard, and ask them questions about how the data is stored, how it can be read, what limitations there are on seeks, etc etc.
It's not the coding aspect that interviewers are interested in, but the problem solving aspect.
Recursion is easy.
struct node {
struct node *left;
struct node *right;
int payload;
};
int is_not_mirror(struct node *one, struct node *two)
{
if (!one && !two) return 0;
if (!one) return 1;
if (!two) return 1;
if (compare(one->payload, two->payload)) return 1;
if (is_not_mirror(one->left, two->right)) return 1;
if (is_not_mirror(one->right, two->left)) return 1;
return 0;
}
It's a pretty normal binary tree, except for the fact that one of the nodes may be empty.
I'd like to find a way to output it in a horizontal way (that is, the root node is on the left and expands to the right).
I've had some experience expanding trees vertically (root node at the top, expanding downwards), but I'm not sure where to start, in this case.
Preferably, it would follow these couple of rules:
If a node has only one child, it can be skipped as redundant (an "end node", with no children, is always displayed)
All nodes of the same depth must be aligned vertically; all nodes must be to the right of all less-deep nodes and to the left of all deeper nodes.
Nodes have a string representation which includes their depth.
Each "end node" has its own unique line; that is, the number of lines is the number of end nodes in the tree, and when an end node is on a line, there may be nothing else on that line after that end node.
As a consequence of the last rule, the root node might be better off in either the top left or the bottom left corner; top left is preferred.
For example, this is a valid tree, with six end nodes (node is represented by a name, and its depth): EDIT: Please see bottom of question for an alternative, easier rendering
[a0]-----------[b3]------[c5]------[d8]
\ \ \----------[e9]
\ \----[f5]
\-[g1]--------[h4]------[i6]
\ \--------------------[j10]
\-[k3]
Which represents the vertical, explicit binary tree:
0 a
/ \
1 g *
/ \ \
2 * * *
/ \ \
3 k * b
/ / \
4 h * *
/ \ \ \
5 * * f c
/ \ / \
6 * i * *
/ / \
7 * * *
/ / \
8 * * d
/ /
9 * e
/
10 j
(branches folded for compactness; * representing redundant, one-child nodes; note that *'s are actual nodes, storing one child each, just with names omitted here for presentation sake)
(also, to clarify, I'd like to generate the first, horizontal tree; not this vertical tree)
I say language-agnostic because I'm just looking for an algorithm; I say ruby because I'm eventually going to have to implement it in ruby anyway.
Assume that each Node data structure stores only its id, a left node, and a right node.
A master Tree class keeps tracks of all nodes and has adequate algorithms to find:
A node's nth ancestor
A node's nth descendant
All end-node descendants of a node, and their count
The generation of a node
The lowest common ancestor of two given nodes
I already know:
The number of end nodes
Anyone have any ideas of where I could start? Should I go for the recursive approach? Iterative?
Some Psuedo-code would be pretty cool too, and much appreciated =)
progress
As per walkytalky's suggestion, I decided to see what it would look like to map each "relevant" or significant node to a grid, with the columns being the depth and the rows identifiable by their end nodes. Here is what happens (skipping column 7 because there are no significant nodes in depth 7):
depth: 0 1 2 3 4 5 6 8 9 10
a b c d
e
f
g h i
j
k
It should be easy enough to generate this grid, with either breadth-first or depth-first searches. Perhaps most trivially by simply keeping a 2D array and placing every significant node found into it, inserting a row for every "second child".
Now, knowing these facts:
The last node in a row must be an end node
Children are always to the right, and on the same row or lower, of their parent node.
All non-end nodes must have exactly two children
Therefore, all non-end nodes have children that are the first to the right of their column, the first child being on the same row, the second child being n rows below them, where n is the number of nodes on the right side of it.
We can see that, given any valid grid, there is one unambiguous way to "connect the dots", so to speak; there is one unambiguous tree being represented.
Now, the "connecting the dots" is no longer a binary-tree-structure question...it's simply a decoration question. We just need to build an algorithm to properly place the right -'s and \'s where they can go, perhaps following only simple grid/lexicographical rules, instead of binary-tree-structure rules.
Basically, this means that the problem of rendering a tree is now the much simpler problem of rendering a grid, with fancy decorations.
Can anyone suggest any way of formulating these rules? Or maybe a completely different method altogether?
edit
I have conceived of a much, much easier final rendering:
--d0----d1----d3----d4----d5----d6----d8----d9----d10-- => guide line (not rendered)
[a0 ]-------[b3 ]-------[c5 ]-------[d8 ]
| | \---------------[e9 ]
| \---------[f5 ]
\---[g1 ]-------[h4 ]-------[i6 ]
| \---------------------------[j10]
\---[k3 ]
--d0----d1----d3----d4----d5----d6----d8----d9----d10-- => guide line (not rendered)
It might be easier to try to create this one, instead of the one I had posted earlier. For one, it preserves a pretty grid shape, and you don't have to fickle with diagonal lines. The rows are all mapped along clearly visible column lines. Unfortunately, it is nowhere near as pretty as the first.
If there are N end nodes, there must be N-1 internal nodes with 2 children. (There can be any number of internal nodes with 1 child, which we will have to count to get the depths but otherwise ignore.) Generating the tree is thus equivalent to positioning these nodes on a grid, where:
the number of rows in the grid is N
I think the number of columns is between 1+floor(log2(N)) and 2*N-1, depending on how much overlap there is; this probably doesn't matter much for our purposes, though
each endpoint appears on a different row
all nodes at the same depth appear in the same column
all internal nodes appear on the same row as their rightmost descendant endpoint
So, let's see:
Walk the tree depth-first, right-to-left.
For each endpoint, record its depth and label.
For each 2-child internal, record its depth, label and the indices of both rightmost and leftmost child endpoints.
Sort the whole lot by depth -- this gives you the column ordering, with the number of distinct depths giving the actual number of columns. (All other ordering should come out automatically from the walk, I think, but that's not the case here because any branch can be any depth.)
Place all the nodes in the grid.
Mark empty cells to the right of each non-endpoint node as horizontal branches.
Mark empty cells down from each internal node to the row above its left child as vertical branches, and the cell at the level of the left child as a junction.
Print with appropriate ASCII decoration.
Update:
As you say, the positioning is enough to unambiguously determine the connections, but you still need to do some bottom-up work to get that right, so I'd probably still do the "mark" steps during the grid building.
I sort of thought the printing was trivial enough to gloss over, but:
Iterate down each column and determine the column width as size of fixed elements + max label length + floor(log10(depth) + 1). (Fixed elements might be [ and ]-, for example. We can substitute ]\n as the suffix for endpoints.)
For each row
for each column
if cell contains a node or endpoint
print fixed prefix
print label
print depth
print fill spaces (max label length - current label length)
print appropriate suffix
if node is an endpoint, skip to next row
if cell is empty, print fill spaces to width of column
if cell contains a vertical, print some chosen prefix number of spaces, a bar, and fill with spaces
if cell contains a junction, print some chosen prefix number of spaces, a backslash, and fill with hyphens
if cell contains a horizontal, print full column width of hyphens
Converting this to print diagonals might be easiest if you generate the straight version first and then do some substitutions in the character array -- otherwise you can get cases where you're rendering a long vertical branch in a different column than the one in which it originated.
At some point I may try to put this into code, but it probably won't be today -- stuff to do!
Looks like an interesting problem; I'd be happy to give it a try, if I had more time.
I'd probably go with the following approach :
Start rendering "right" (or in your case, "top") nodes, until I reach the end. (i.e.: render a, b, c, and d)
Go back to the last node with a child (i.e.: c), and do the same thing recursively
You would have to keep a global variable indicating on wich row you are printing. Each recursive call increases this variable.
edit: ok, couldn't resist trying to write some untested pseudo-code, hope it works:
function print_tree(Node n) {
print "\n" // begin on a fresh new line
childs = new Array();
do {
if (n.hasLeftChild) {
childs.push(n.leftChild)
}
print "---" + n.id //this needs a lot of tweaking, but you get the idea
} while(n = n.rightChild)
childs.reverse()
foreach(child in childs) {
print_tree(child);
}
}
If you start with a label width for each level (not including [] characters), equal to the largest label for that width (in this example the widths are mostly 2 except j10 which is 3, and levels 2 and 7 which are 0).
Have each level with non-zero max label width equally spaced with one - character between each level, so you can calculate initial level y locations.
Give each node it's line number.
Then adjust the level locations based on the maximum number of lines between children for a level.
Added 2 to level 1 for a0 to g1
Added 1 to level 2 for g1 to k3
Added 1 to level 4 for b3 to [ ]
Use \ and ` characters for diagonals.
[a0]---------[b3]-------[c5]------[d8]
\ \ `----------[e9]
\ `-----[f5]
`[g1]--------[h4]------[i6]
\ `--------------------[j10]
`[k3]
Below is fully functional C# code that does exactly what you want. How it does it:
The tree is represented as objects from classes that inherit from Node
First compute the number of leaves and create an array of that much lines
Then for each level:
find out on what lines are we going to write
for those lines, compute the maximum of what is already on those lines
write the all the nodes to column max(number from previous step, end of previous level)+1; prepend with - to get to that column
write diagonal lines from all binary nodes up to the line of their right child (in my program first child is left, second is right, you have it the other way around)
advance one level
The algorithm makes sure that each level starts only after previous ends. That is probably good choice for short names, but for longer names, this probably shouldn't be enforced.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SO_ASCII_tree
{
class Program
{
static void Main()
{
Node root = …;
StringBuilder[] lines = Enumerable.Range(0, root.Leaves).Select(i => new StringBuilder()).ToArray();
Node[] currentLevel = new Node[] { root };
int level = 0;
int min = 0;
int max = 0;
while (currentLevel.Any())
{
NamedNode[] namedNodes = currentLevel.OfType<NamedNode>().ToArray();
if (namedNodes.Any())
{
min = namedNodes.Select(node => lines[node.Line].Length).Max();
min = Math.Max(min, max);
if (min != 0)
min++;
foreach (NamedNode namedNode in namedNodes)
WriteAtPosition(lines[namedNode.Line], namedNode.Write(level), min, '-');
max = namedNodes.Select(node => lines[node.Line].Length).Max();
// change to max = min + 1; for long names
}
foreach (Node node in currentLevel)
node.SetChildLines();
Binary[] binaries = namedNodes.OfType<Binary>().ToArray();
foreach (Binary binary in binaries)
GoDown(lines, binary.Line, binary.Right.Line);
currentLevel = currentLevel.SelectMany(node => node.Children).ToArray();
level++;
}
foreach (StringBuilder line in lines)
Console.WriteLine(line.ToString());
}
static void WriteAtPosition(StringBuilder line, string message, int position, char prepend = ' ')
{
if (line.Length > position)
throw new ArgumentException();
line.Append(prepend, position - line.Length);
line.Append(message);
}
static void GoDown(StringBuilder[] lines, int from, int to)
{
int line = from + 1;
int position = lines[from].Length;
for (; line <= to; line++, position++)
WriteAtPosition(lines[line], "\\", position);
}
}
abstract class Node
{
public int Line
{ get; set; }
public abstract int Leaves
{ get; }
public abstract IEnumerable<Node> Children
{ get; }
public virtual void SetChildLines()
{ }
}
abstract class NamedNode : Node
{
public string Name
{ get; set; }
public string Write(int level)
{
return '[' + Name + level.ToString() + ']';
}
}
class Binary : NamedNode
{
public Node Left
{ get; set; }
public Node Right
{ get; set; }
int? leaves;
public override int Leaves
{
get
{
if (leaves == null)
leaves = Left.Leaves + Right.Leaves;
return leaves.Value;
}
}
public override IEnumerable<Node> Children
{
get
{
yield return Left;
yield return Right;
}
}
public override void SetChildLines()
{
Left.Line = Line;
Right.Line = Line + Left.Leaves;
}
}
class Unary : Node
{
public Node Child
{ get; set; }
int? leaves;
public override int Leaves
{
get
{
if (leaves == null)
leaves = Child.Leaves;
return leaves.Value;
}
}
public override IEnumerable<Node> Children
{
get
{
yield return Child;
}
}
public override void SetChildLines()
{
Child.Line = Line;
}
}
class Leaf : NamedNode
{
public override int Leaves
{
get
{
return 1;
}
}
public override IEnumerable<Node> Children
{
get
{
yield break;
}
}
}
}
EDIT: Your example tree gets rendered exactly the same as your rendering:
[a0]-----------[b3]------[c5]------[d8]
\ \ \----------[e9]
\ \----[f5]
\-[g1]--------[h4]------[i6]
\ \--------------------[j10]
\-[k3]
You'd probably need to perform a depth first search if not a search of the entire tree in order to properly size it for output along 2 dimensions.