What is the difference between a balanced binary tree and a complete binary tree?
Is it true to say every complete binary tree is a balanced tree?
How about the other way around?
A balanced binary tree is the binary tree where the depth of the two subtrees of every node never differ by more than 1.
A complete binary tree is a binary tree whose all levels except the last level are completely filled and all the leaves in the last level are all to the left side.
Below is a balanced binary tree but not a complete binary tree. Every complete binary tree is balanced but not the other way around.
1
1 1
1 1 1
1
As implies, in a complete tree, always the level difference will be no more than 1 so it is always balanced.
Since this developed into further questions about perfect, balanced, complete, and full - here is an answer that clarifies those as well. Assuming a a binary tree,
Balanced: The left and right subtrees of every node differ in height by no more than 1.
Full: All nodes except leaf nodes have either 0 or 2 children
Complete:
All nodes except for the level before the last must have 2 children.
All nodes in the last level are as far left as possible.
Summary:
Complete trees: must be balanced; can be full
Full trees: can be balanced; can be complete
Balanced trees: can be complete; can be full
Examples:
Full & balanced - All nodes have 0 or 2 children, level 3 - level 2 <= 1, (Not complete - last level nodes are not as far left as possible)
1 --- LEVEL 0
/ \
1 1 --- LEVEL 1
/\ /\
1 1 1 1 --- LEVEL 2
- /\ - -
1 1 --- LEVEL 3
x x - -
Full, balanced & complete - All nodes have 0 or 2 children, 3 - 2 <= 1, last level nodes are as far left as possible:
1 --- LEVEL 0
/ \
1 1 --- LEVEL 1
/\ /\
1 1 1 1 --- LEVEL 2
/\ - - -
1 1 --- LEVEL 3
- -
Full - All nodes have 0 or 2 children (Unbalanced - 3 - 1 > 1, Not complete - level 1 has a node with 0 children):
1 --- LEVEL 0
/ \
1 1 --- LEVEL 1
/ \ -
1 1 --- LEVEL 2
/ \ - x x
1 1 --- LEVEL 3
- -
Complete & balanced - Last level nodes are as far left as possible, 3 - 3 <= 1 (Not full - there is a level 2 node with 1 child):
1 --- LEVEL 0
/ \
1 1 --- LEVEL 1
/\ /\
1 1 1 1 --- LEVEL 2
/\ /\ /\ /x
1 1 1 11 1 1 --- LEVEL 3
- - - -- - -
Balanced - 3 - 3 <= 1, (Not full - there is a level 2 node with 1 child, Not complete - last level nodes are not as far left as possible)
1 --- LEVEL 0
/ \
1 1 --- LEVEL 1
/\ /\
1 1 1 1 --- LEVEL 2
/\ /\ /x /\
1 11 11 1 1 --- LEVEL 3
- -- -- x - -
Perfect: All interior nodes have two children and all leaves have the same depth.
None of the above examples are perfect
Balanced Tree : A balanced tree is that form of binary tree in which the difference of left subtree's height and right subtree's height at every node will be atmost k, where k will be the balancing factor . If this balancing factor turn out to be 0 then that tree will be called fully balanced tree .
Example :
8
6 1
3 9 1
This tree is balanced tree with balanced factor 1 as diff in height of each node's subtree is either 0 or 1.
Complete binary tree: A tree is said to be complete if apart from leaf's each level is completely filled . And any new insertion on the leaf node will be from left to right which means leaf at left level will be filled completely first.
Example :
8
6
1
3 5 4 1
Now this tree is complete binary tree and if any new insertion has to be done then it should be under leaf at left which is 3 in this example . Once 3 is filled with left and right child then 5 will be the next leaf for new insertion.
Tree is said to be full when a a binary tree of height h has all of its leaves at level h and every parent has exactly two children
Tree is said to be complete when all levels but the last contain as many nodes as possible, and the nodes on the last level are filled in from left to tight. (Not full, but complete)
When each node in a binary tree has two subtrees who's heights are exactly the same the tree is said to be completely balanced
Completely balanced trees are full
A tree is height balanced or simply balanced if the subtrees of a node differ by no more than one
Related
Given a tree, find the common subtrees and replace the common subtrees and compact the tree.
e.g.
1
/ \
2 3
/ | /\
4 5 4 5
should be converted to
1
/ \
2 3
/ | /\
4 5 | |
^ ^ | |
|__|___| |
|_____|
this was asked in my interview. The approach i shared was not optimal O(n^2), i would be grateful if someone could help in solutioning or redirect me to a similar problem. I couldn't find any. Thenks!
edit- more complex eg:
1
/ \
2 3
/ | /\
4 5 2 7
/\
4 5
whole subtree rooted at 2 should be replaced.
1
/ \
2 <--3
/ | \
4 5 7
You can do this in a single DFS traversal using a hash map from (value, left_pointer, right_pointer) -> node to collapse repeated occurrences of the subtree.
As you leave each node in your DFS, you just look it up in the map. If a matching node already exists, then replace it with the pre-existing one. Otherwise, add it to the map.
This takes O(n) time, because you are comparing the actual pointers to the left + right subtrees, instead of traversing the trees to compare them. The pointer comparison gives the same result, because the left and right subtrees have already been canonicalized.
Firstly, we need to store the node values that appear in a hash table. If the tree already exists, we can iterate the tree and if a node value is already in the set of nodes and delete the branches of that node. Otherwise, store the values in a hash map and each time, when a new node is made, check if the value appears in the map.
This was a quiz question. I'm not sure whether my answer was right. Please help me out.
Lets say the height is h, since no two consecutive nodes (as we go up the tree) can be red, wouldn't the max number of red nodes be h/2? (h = log n)
Somehow, I feel that is not the correct answer.
Any help/input would be greatly appreciated!
Thank you so much in advance!
** Edit ** This answer assumes a definition of height to be the number of nodes in the longest path from root to leaf (used, e.g., in lecture notes here) including the "virtual" black leaf nodes. More common definition counts the number of edges, and does not include the leaf nodes. With this definition the answer is round(h/2), and if you include the leaf nodes in to height round_down(h/2). ** Edit ends **
If you follow the rules that the root node is black as in Wikipedia, then the correct answer is the largest integer smaller than h/2. This is just because root and leaves are black, and half of the nodes (rounded up) in between can be red. I.e. round((h-2)/2)
You can also find the rule just by considering some small red-black trees of different heights.
Case h=1 root is black -> 0 red nodes
Case 'h=2' root is black and leaves are black -> 0 red nodes
Case h=3 root is black, second level can be red, and leaves must be black -> max 1 red node
Case h=4 root is black, second level can be red, third level must be black, and leaves must be black -> max 1 red node
Case h=5 black, red, black, red, black -> max 2 red nodes.
The h as a function of n is trickier, but it can be shown that h <= 2 log (n+1), which guarantees the logarithmic search time. For a proof see, e.g., Searching and Search Trees II (page 11). The proof is based on the fact that the rules of red-black tree guarantee that a subtree starting at x contains at least 2^(bh(x)) - 1 internal nodes, where bh(x) is the black height - number of black nodes in path from root to leaf. This is proven by induction. Then by noting that at most half of the nodes are black (we are speaking of subtrees so the root can be red) that bh(x) >= h/2. Now using these results we get n >= 2^bh(x) - 1 >= 2^(h/2) -1. Solving for h, we get the answer h <= 2 log(n+1).
As the question was a quiz, it should be enough to say that h is proportional to log(n) or even about log(n).
Let's first see how few nodes (minimising n) are needed to make a path with 1 red node (* is black):
*
/ \
* R
/ \
* *
So n must be at least 5 when 1 red node is needed. It has 3 leaf nodes, and 2 internal nodes. Removing any node will require to drop the red node as well to stay within the rules.
If we want to extend this tree to get a path with 2 red nodes we could apply the following two steps:
All leaves get two black children
The right-most leaf (just added) is turned into a red node, and it gets 2 black children.
The dollar signs are the added black nodes compared to the prevous tree:
*
/ \
* R
/| / \
$ $ * *
/| / \
$ $ $ R
/ \
$ $
We choose to place that path with the red nodes on the right side; this choice does not influence the conclusions. Note that it does not help to add red nodes in other, shorter paths, as this will only increase the number of nodes without increasing the path with the most red nodes.
The number of leaf nodes (L) doubles with step 1, while the nodes that were leaves become internal nodes (I).
The second step increases both the number of internal nodes and number of leaves with 1. More formally put, we can find these formulas, where the index r represents the number of red nodes:
L1 = 3
I1 = 2
Lr+1 = 2Lr + 1
Ir+1 = Ir + Lr + 1
Put in a table for increasing r:
r | L | I | n=L+I
----+-----+-----+-------
1 | 3 | 2 | 5
2 | 7 | 6 | 13
3 | 15 | 14 | 29
4 | 31 | 30 | 61
... | ... | ... | ...
We can see the following is true:
Lr = 2r+1 - 1
Ir = 2r+1 - 2
And so:
nr = 2r+2 - 3
So we have a formula for knowing the minimum number of nodes needed to have a path with r red nodes. We need a different relation: the maximum for r when given n.
From the above we can derive:
r = ⌊ log2(n+3) ⌋ - 2
Given a binary search tree t, it is rather easy to get its depth using recursion, as the following:
def node_height(t):
if t.left.value == None and t.right.value == None:
return 1
else:
height_left = t.left.node_height()
height_right = t.right.node_height()
return ( 1 + max(height_left,height_right) )
However, I noticed that its complexity increases exponentially, and thus should perform very badly when we have a deep tree. Is there any faster algorithm for doing this?
If you store the height as a field in the Node object, you can add 1 as you add nodes to the tree (and subtracting during remove).
That'll make the operation constant time for getting the height of any node, but it adds some additional complexity into the add/remove operations.
This kind of extends from what #cricket_007 mentioned in his answer.
So, if you do a ( 1 + max(height_left,height_right) ), you end up having to visit every node, which is essentially an O(N) operation. For an average case with a balanced tree, you would be looking at something like T(n) = 2T(n/2) + Θ(1).
Now, this can be improved to a time of O(1) if you can store the height of a certain node. In that case, the height of the tree would be equal to the height of the root. So, the modification you would need to make would be to your insert(value) method. At the beginning, the root is given a default height of 0. The node to be added is assigned a height of 0. For every node you encounter while trying to add this new node, increase node.height by 1 if needed, and ensure it is set to 1 + max(left child's height, right child's height). So, the height function will simply return node.height, hence allowing for constant time. The time complexity for the insert will also not change; we just need some extra space to store n integer values, where n is the number of nodes.
The following is shown to give an understanding of what I am trying to say.
5 [0]
- insert 2 [increase height of root by 1]
5 [1]
/
/
[0] 2
- insert 1 [increase height of node 2 by 1, increase height of node 5 by 1]
5 [2]
/
/
[1] 2
/
/
[0] 1
- insert 3 [new height of node 2 = 1 + max(height of node 1, height of node 3)
= 1 + 0 = 1; height of node 5 also does not change]
5 [2]
/
/
[1] 2
/ \
/ \
[0] 1 3 [0]
- insert 6 [new height of node 5 = 1 + max(height of node 2, height of node 6)
= 1 + 1 = 2]
5 [2]
/ \
/ \
[1] 2 6 [0]
/ \
/ \
[0] 1 3 [0]
For example, we are provided with only post order traversal array or only pre order traversal array. Can we reconstruct the binary tree back? If we know that the binary tree is full. Moreover, if it is not, is it possible to construct the full binary if know both preorder and post order at the same time?
No, you can't from one list alone.
think of the postorder list: 4 5 2 3 1
1 1
/ \ / \
2 3 4 3
/ \ / \
4 5 5 2
both trees are possible, but we don't know which one generated the list
Assuming every element in the tree is unique, we know that preorder is build like that:
[Node][ LeftTree ][ RightTree ]
and postorder like this:
[ LeftTree ][ RightTree ][Node]
if we have two lists, preorder 1 2 4 5 3 and postorder 4 5 2 3 1, we know that 1 is the root of the tree, because it is the first number of the preorder list (and the last number of the postorder list). Furthermore we know that 2 must be the root of the left tree and 3 the root of the right tree, because they are the first numbers after the root node which are roots of the left or right tree. With this in mind we can split the lists into this:
[Root in preorder] [ LeftTree ] [RightTree] [Root in postorder]
preorder: [1] [2 4 5] [3]
postorder: [4 5 2] [3] [1]
From here you can do this algorithm recursively with the left and right tree and in the end you get this:
1
/ \
2 3
/ \
4 5
Since every element is unique there is only one way to build the tree and therefore you can rebuild a tree from its postorder and preorder list.
In case you have elements that are the same you can't build a unique tree, example:
preorder: 1 X X 5 X
postorder: X 5 X X 1
from these lists you could create these two trees:
1 1
/ \ / \
X X X X
/ \ / \
X 5 5 X
I was playing with the orders a bit to understand them better and here are my findings:
Post-order - from the sequence you can always tell what is the root and what is the right-most child (ex. is 1,2,3,4,5 - 5 is the root and 4 is the right-most child)
Pre-order - from the sequence you can always tell what is the root and what is the left-most child (ex. is 1,2,3,4,5 - 1 is the root and 2 is the left-most child)
In-order - given a root vertex, you can always tell what is on the left and what is on the right (ex. is 1,2,3,4,5 and for the root 3 - 1,2 are on the left, 3,4,5 are on the right )
Now you can play with it. Having an in-order with either post-order or pre-order, you can easily reconstruct the tree because you can find the root and recursively find it always for the left/right branch. In case of having pre-order and post-order together, you can find the root and left-most children & right-most children. The problem happens in case the root has only left/right children, because you cannot tell which one it is and as such, you cannot easily reconstruct the tree.
However, as being asked, having the "full" binary tree, where every vertex has either both children or any, you don't have the problem with pre/post order combination and therefore every pair of orders will help you to reconstruct the tree. However only having one of the orders, you cannot reconstruct the tree (for example knowing only left child is not enough, you don't have information about which is the right one)
Suppose you had a tree w/ 3 nodes, all labelled the same. There are at least 3 such (not-necessarily full) trees, but all will have the same traversal arrays, no matter what order. That should answer your first question.
I'm constructing a binary tree for a sequence of data and the tree is stored in a 1-based array. So if index of parent node is idx,
the left child is 2 * idx and the right is 2 * idx + 1.
Every iteration, I sort current sequence based on certain criteria, select the median element as parent, tree[index] = sequence[median], then do same operation on left(the sub sequence before median) and right(the subsequence after median) recursively.
Eg, if 3 elements in total, the tree will be:
1
/ \
2 3
the array size to store the tree is also 3
4 elements:
1
/ \
2 3
/
4
the array size to store the tree is also 4
5 elements:
1
/ \
2 3
/ \ /
4 null 5
the array size to store the tree has to be 6, since there is a hole between 4 and 5.
Thus, the array size is only determined by number of elements, I believe there is an anlytical solution for it, just can't prove it.
Any suggestion will be appreciated.
Thanks.
Every level of a binary tree contains twice as many nodes as the previous level. If you have n nodes, then the number of levels required (the height of the tree) is log2(n) + 1, rounded up to a whole number. So if you have 5 nodes, your binary tree will have a height of 3.
The number of nodes in a full binary tree of height h is (2^h) - 1. So you know that the maximum size array you need for 5 items is 7. Assuming all the levels are filled except possibly the last one.
The last row of your tree will contain (2^h)-1 - n nodes. The last level of a full tree contains 2^(h-1) nodes. Assuming you want it balanced so half of the nodes are on the left and half are on the right, and the right side is left-filled, that is, you want this:
1
2 3
4 5 6 7
8 9 10 11
The number of array spaces required required for the last level of your tree, then, is either 1, or it's half the number required by a full tree, plus half the nodes required by your tree.
So:
n = 5
height = roundUp(log2(n) + 1)
fullTreeNodes = (2^height) - 1
fullTreeLeafNodes = 2^(height-1)
nodesOnLeafLevel = fullTreeNodes - n
Now comes the fun part. If there is more than 1 node required on the leaf level, and you want to balance the sides, you need half of fullTreeLeafNodes, plus half of nodesOnLeafLevel. In the tree above, for example, the leaf level has a potential for 8 nodes. But you only have 4 leaf nodes. You want two of them on the left side, and two on the right. So you need to allocate space for 4 nodes on the left side (2 for the left side items, and 2 empty spaces), plus two more for the two right side items.
if (nodesOnLeafLevel == 1)
arraySize = n
else
arraySize = (fullTreeNodes - fullTreeLeafNodes/2) + (nodesOnLeafLevel / 2)
You really shouldn't have any holes. They are created by your partitioning algorithm, but that algorithm is incorrect.
For 1-5 items, your trees should look like:
1 2 2 3 4
/ \ / \ / \ / \
1 1 3 2 4 2 5
/ / \
1 1 3
The easiest way to populate the tree is to do an in-order traversal of the node locations, filling items from the sequence in order.
I'm close to formalizing a solution. By intuition, first find the maximal power of 2 < N, then check whether the N - 2^m is even or odd, decide which part of the leave level need be growed.
int32_t rup2 = roundUpPower2(nPoints);
if (rup2 == nPoints || rup2 == nPoints + 1)
{
return nPoints;
}
int32_t leaveLevelCapacity = rup2 / 2;
int32_t allAbove = leaveLevelCapacity - 1;
int32_t pointsOnLeave = nPoints - allAbove;
int32_t iteration = roundDownLog2(pointsOnLeave);
int32_t leaveSize = 1;
int32_t gap = leaveLevelCapacity;
for (int32_t i = 1; i <= iteration; ++i)
{
leaveSize += gap / 2;
gap /= 2;
}
return (allAbove + leaveSize);