Which tree is balance and AVL balanced? - binary-tree

From my understanding, a complete tree is a balanced tree that may not be AVL balanced.
A complete tree is one where all levels are completely filled except maybe the deepest, at depth n, the height of the tree needs to be as far left as possible.
Am I therefore right to say that both these trees are Complete and AVL balanced?
Example A:
4
/ \
2 5
/ /
1 3
Example B:
4
/ \
2 5
/ \
1 3

Example A is neither complete, nor AVL.
It is not complete because at the bottom level not all values are as far left as possible (there is a gap). If you would iterate the bottom level and include null for where the parent node has no corresponding child, it would be 2,null,3,null,... while there should be no value occurring after such null.
It is not AVL, because it violates the order: no nodes in the right subtree of 4 should be less than 4.

Related

Unable to understand why this is not a height balanced binary search tree

I am looking at LeetCode problem 110. Balanced Binary Tree:
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as:
a binary tree in which the left and right subtrees of every node differ in height by no more than 1.
I have this input:
[2,1,3,0,null,null,4,null,null,null,5]
For this input, the expected output is false. Why is this not a height balanced binary search tree? I did look up the definition of what height balanced means, but maybe I am unclear of the direction in which to look at this tree to determine whether it is height balanced or not.
The tree represented by this array is:
2
/ \
1 3
/ \
0 4
\
5
The definition of what balanced means in this LeetCode exercise is:
a binary tree in which the left and right subtrees of every node differ in height by no more than 1.
This is not true for the tree rooted at 3. Its left and right subtrees differ in height by 2.

Depth vs Level of a Tree

Are they the same thing?
In this article Height,Depth and Level of a Tree
Depth is defined as the number of edges from a node to the tree's root node while
Level is defined as
1 + the number of connections between the node and the root."
or basically depth + 1
and in this link
What is level of root node in a tree?
It is said that level can either start with 1 or 0 which makes it the same with depth if it starts with 0
So which is which? If it is 1 + depth then what is the use of adding 1?
well, it will be best explained by a image, just see below:
// I've used 1 for roots level
// though some people consider roots level as 0, so you can use either 0 or 1
// I would prefer to use 1
// but its your choice
o(depth=0, height=3, lev=1)
/ \
(depth=1, height=2, lev=2)o o(depth=1, height=1, lev=2)
/ / \
(depth=2, height=1, lev=3)o o o(depth=2, height=0, lev=3)
/
(depth=3, height=0, lev=4)o
I hope its clear to you now...
The question was asked because there were already a lot about height vs depth of a tree but not a clear distinction in level and depth of a tree and is often times used interchangeably.
So as I have read here, in different articles and in a book;
The level is depth + 1. It is not the same with depth although some choose to start the level with 0.
Depth is mostly used in relation to the root as
Depth is the number of edges from the root to a node
So it is mostly treated as a property of a node while the level is mostly used as a whole e.g.
Width is the number of nodes in a level
Or in
A perfect binary tree is where all internal nodes have two children and all leaves are at the same level
So level is like steps in a tree wherein the root node is the first step and it just so happen that it shared the same pattern with the depth of a node.
Although there is no single definition, to distinguish the two the level is mostly taken as depth + 1.

Given the level order traversal of two complete binary trees, how to check whether one tree is mirror of other?

How to check whether two complete binary trees are mirror of each other where only the level order traversal of the trees are given ?
A Complete binary tree is a binary tree which all the nodes except the leaf nodes have 2 child nodes.
I don't think this is possible. Consider these two trees:
0 0
/ \ / \
1 2 1 2
/ \ / \ / \
3 4 3 4 5 6
/ \
5 6
These are complete binary trees (according to your definition), and even though they're different trees they have the same level-order traversals: 0123456.
Now, look at their mirrors:
0 0
/ \ / \
2 1 2 1
/ \ / \ / \
4 3 6 5 4 3
/ \
6 5
Notice that the left tree has level-order traversal 0214365, while the right tree has level-order traversal 0216543. In other words, the original trees have the same level order traversals, but their mirrors have different traversals.
Now, think about what happens if you have your algorithm and you feed in 0123456 (the level-order traversal of either of the trees) and 0214365 (the level-order traversal of one of the mirrors). What can the algorithm say? If it says that they're mirrors, it will be wrong if you fed in the second of the input trees. If it says that they're not mirrors, it will be wrong if you fed in the first of the input trees. Therefore, there's no way for the algorithm to always produce the right answer.
Hope this helps!
According to general definitions, in a complete binary tree, every level except possibly the last, is completely filled, and all nodes are as far left as possible. So a complete binary tree may have a node with just one child (for eg, one root node with one left child is a complete binary tree). A tree where all nodes except the leaves have 2 child nodes is called a full binary tree.
For complete binary trees, the problem would be trivial. Starting from the top-down, for the ith level you need to compare 2^i elements (root being the 0th level) of the given level-order traversals A and B. For any given i, the set of 2^i elements from A should be equal to the reverse of these elements from B. However, the last level may not be completely filled and you'll need to account for that.
For full binary trees, where the only constraint given is that every node has 2 or no children, it won't be possible unless you have constructed the tree itself. And you cannot construct a tree by using only level-order traversal. templatetypedef provides a good example.

Ordering binary search trees

When trying to order a set of numbers into a binary search tree, is there always exactly one way to order them so the tree has the shortest height, in other words most efficient?
A set of numbers can be converted to a BST by taking one element as the root of the tree and arranging all other numbers around it. I could see the following situation contradicting this theory:
Picking one root leads to a tree of height h, with the left subtree being 'taller' than the right subtree.
Picking another root leads to a different tree, also of height h, with the right subtree being 'taller' than the left subtree.
Another simple example involves swapping the order of insertion of two consecutive elements that are not directly related, and thus do not affect each other's position in the tree.
Disproof by counter-example.
Let the set S = {0, 1, 2, 3}.
Insert the elements into a binary search tree in the following order: 1, 0, 2, 3
1
/ \
0 2
\
3
Insert the elements into a binary search tree in the following order: 1, 2, 0, 3
1
/ \
0 2
\
3
Because these two trees have different orders of insertion, and yet both have minimum height, the statement that there is only one order of insertion that provides a binary search tree of minimum height is false.
If the actual ordering of elements on the tree is what you're concerned about, insert the elements of the set in the following order: 2, 1, 0, 3
2
/ \
1 3
/
0
Again, this tree has the same height as the previous trees, thus showing that a different ordering of items in the tree can also produce a tree of minimum height.
(An aside)
You can always build a minimum height tree by first sorting the elements of the set, then continually subdividing the sorted set to ensure balance and complete filling of each row.
Take the median element of the set. In the case of an even number of elements, take the larger of the two 'middle' elements. This will become the root of the tree.
Take all the elements below the median. This will become the left subtree of the root.
Take all the elements above the median. This will become the right subtree of the root.
Recursively create the left and right subtrees from these sets.
This should ensure that you have a complete binary tree, which will always be of minimum height.

Disjoint-set forests - why should the rank be increased by one when the find of two nodes are of same rank?

I am implementing the disjoint-set datastructure to do union find. I came across the following statement in Wikipedia:
... whenever two trees of the same rank r are united, the rank of the result is r+1.
Why should the rank of the joined tree be increased by only one when the trees are of the same rank? What happens if I simply add the two ranks (i.e. 2*r)?
First, what is rank? It is almost the same as the height of a tree. In fact, for now, pretend that it is the same as the height.
We want to keep trees short, so keeping track of the height of every tree helps us do that. When unioning two trees of different height, we make the root of the shorter tree a child of the root of the taller tree. Importantly, this does not change the height of the taller tree. That is, the rank of the taller tree does not change.
However, when unioning two trees of the same height, we make one root the child of the other, and this increases the height of that overall tree by one, so we increase the rank of that root by one.
Now, I said that rank was almost the same as the height of the tree. Why almost? Because of path compression, a second technique used by the union-find data structure to keep trees short. Path compression can alter an existing tree to make it shorter than indicated by its rank. In principle, it might be better to make decisions based on the actual height than using rank as a proxy for height, but in practice, it is too hard/too slow to keep track of the true height information, whereas it is very easy/fast to keep track of rank.
You also asked "What happens if I simply add the two ranks (i.e. 2*r)?" This is an interesting question. The answer is probably nothing, meaning everything will still work just fine, with the same efficiency as before. (Well, assuming that you use 1 as your starting rank rather than 0.) Why? Because the way rank is used, what matters is the relative ordering of ranks, not their absolute magnitudes. If you add them, then your ranks will be 1,2,4,8 instead of 1,2,3,4 (or more likely 0,1,2,3), but they will still have exactly the same relative ordering so all is well. Your rank is simply 2^(the old rank). The biggest danger is that you run a larger risk of overflowing the integer used to represent the rank when dealing with very large sets (or, put another way, that you will need to use more space to store your ranks).
On the other hand, notice that by adding the two ranks, you are approximating the size of the trees rather than the heights of the trees. By always adding the two ranks, whether they are equal or not, then you are exactly tracking the sizes of the trees. Again, everything works just fine, with the same caveats about the possibility of overflowing integers if your trees are very large.
In fact, union-by-size is widely recognized as a legitimate alternative to union-by-rank. For some applications, you actually want to know the sizes of the sets, and for those applications union-by-size is actually preferabe to union-by-rank.
Because in this case - you add one tree is a "sub tree" of the other - which makes the original subtree increase its size.
Have a look at the following example:
1 3
| |
2 4
In the above, the "rank" of each tree is 2.
Now, let's say 1 is going to be the new unified root, you will get the following tree:
1
/ \
/ \
3 2
|
4
after the join the rank of "1" is 3, rank_old(1) + 1 - as expected.1
As for your second question, because it will yield false height for the trees.
If we take the above example, and merge the trees to get the tree of rank 3. What would happen if we then want to merge it with this tree2:
9
/ \
10 11
|
13
|
14
We'll find out both ranks are 4, and try to merge them the same way we did before, without favoring the 'shorter' tree - which will result in trees with higher height, and ultimately - worse time complexity.
(1) Disclaimer: The first part of this answer is taken from my answer to a similar question (though not identical due to your last part of the question)
(2) Note that the above tree is syntatically made, it cannot be created in an optimized disjoint forests algorithms, but it still demonstrates the issues needed for the answer.
If you read that paragraph in a little more depth, you'll realize that rank is more like depth, not size:
Since it is the depth of the tree that affects the running time, the tree with smaller depth gets added under the root of the deeper tree, which only increases the depth if the depths were equal. In the context of this algorithm, the term "rank" is used instead of "depth" ...
and a merge of equal depth trees only increases the depth of the tree by one since the root of the one is added to the root of the other.
Consider:
A D
/ \ merged with / \
B C E F
is:
A
/|\
B C D
/ \
E F
The depth was 2 for both, and it's 3 for the merged one.
Rank represents the depth of the tree, not the number of nodes in it. When you join a tree with a smaller rank with a tree with a larger rank, the overall rank remains the same.
Consider adding a tree with rank 4 to the root of the tree of rank 6: since we added a node above the root of the depth-4 tree, that subtree now has a rank of 5. The subtree to which we've added our depth-4 tree, however, is 6, so the rank does not change.
Now consider adding a tree with rank 6 to the root of a second tree of rank 6: since the root of the first depth-6 tree now has an extra node above it, the rank of that subtree (and the tree overall) changes to 7.
Since the rank of the tree determines the processing speed, the algorithm tries to keep the rank as low as possible by always attaching a shorter tree to the taller one, keeping the overall rank unchanged. The rank changes only when the trees have identical ranks, in which case one of them gets attached to the root of the other, bumping up the rank by one.
Actually Here two important properties should be known very well to us ....
1) What is Rank ?
2) Why Rank is Used ???
Rank is nothing but the depth of a tree .U can say rank as depth (level) of a tree . When we make union nodes then these (graph nodes ) will be formed as a tree with an ultimate root node.Rank is expressed only for those root nodes .
A merged with D
Initially A has rank (level) 0 and D has rank(level) 0 . So u can merge them making anyone of them as a root . Because if u make A as root the rank(level) will be 1
and if u make D as a root then the rank will also be 1
A
`D
Here rank ( level ) is 1 when root is A .
Now think for another ,
A merge B -----> A
`D `C / \
D B
\
C
So the level will be increased by 1 , see exactly without root (A) there is at most height / depth / rank is 2 . rank[ 1] -> {D,B} and rank [2] -> {C} ................
Now our main objective is to make tree with minimum rank(depth) as possible while merging ..
Now when two differnt rank tree merge ,then
A(rank 0) merge B(rank 1)---> B Here merged tree rank is 1 same as high rank (1)
`C / \
A C
When small rank goes under over high rank . Then the merged tree's rank(height/depth) will be the same rank associated with higher rank tree .That means the rank will not increase , the merged tree rank will be same as higher rank before ...
But if we will do the reverse work means high rank tree goes under over low rank tree then see ,
A ( rank 0 ) merge B (rank 1 ) --> A ( merged tree rank 2 greater than both )
`C `B
`C
So , whatever is seen from following observation is that if we try to keep rank (height) of merged tree as minimum possible then , we have to choose the first process. i think this part is clear !!
Now u have to understand what is our objective to keep tree's height minimum as possible ..........
when we use disjoint set union then for path compression ( finding ultimate root with whom a node is connected ) when we traverse from a node to it's root node then if it's height (rank) is long then time processing will be slow .That's why when we try to merge two trees then we try to keep heigh/depth/rank as minimum as possible

Resources