Red black tree deletion unknown behaviour - algorithm

I entered several numbers to a red black tree. (41; 38; 31;
12; 19; 8) after deleting 8 and 12 (1st screenshot) it got into the type of the second screenshot. I can't understand why that 31 turn into red . Please help me with that ? If you could please mention the case related to that.
Thank you !

If you check the explanation of the removal algorithm on Wikipedia, you can map their naming of nodes to your tree as follows:
M = 0012, the black node to remove
C = a NIL leaf below 0012 (NILs are always considered to be black)
The article goes on to say about your actual case:
The complex case is when both M and C are black. [...] We begin by replacing M with its child C. [...] We will relabel this child C (in its new position) N, and its sibling (its new parent's other child) S [...] we will also use P for N's new parent, SL for S's left child, and SR for S's right child
So now we have after the removal, but before re-colouring:
P = 0019 (red)
N = a NIL leaf, the left child of 0019
S = 0031, the right child of 0019
The description identifies several cases, and the case at hand is the following one:
Case 4: S and S's children are black, but P is red. In this case, we simply exchange the colors of S and P.
The reason for this colour swap is explained:
This does not affect the number of black nodes on paths going through S, but it does add one to the number of black nodes on paths going through N, making up for the deleted black node on those paths.
Recall that in Red Black trees this invariant must be maintained (property 5):
Every path from a given node to any of its descendant NIL nodes contains the same number of black nodes.
This invariant would have been violated if the above colour-swap were omitted.

Related

Calculating the number of black nodes in each path in Red Black Tree

What recursion algorithm should I use to calculate the number of black nodes in each path?
In a red-black tree, the number of black nodes on any path is constant. The process can be broken into two parts.
Part 1: get a path. Here, a path is defined as a sequence of nodes.
To get a path from the empty tree, return the path containing only the empty tree.
To get a path from a non-empty tree, get the path of its left child and append the non-empty tree to this path.
Part 2: count the number of black nodes in the path.
Hopefully, this one is self-explanatory.
What recursion algorithm should I use to calculate the number of black nodes in each path?
None. Use an iterative approach. So if you start from the root, increment a counter initialised to 0 every time you see a black node including the root and keep going leftwards until the left node pointer is null. Whatever the count is, that is the black depth down any given path. In pseudo code:
int bcount = 0;
Node *currnode = GetRoot();
while (currnode != NULL)
{
if (currnode->GetColour() == BLACK)
++bcount:
currnode = currnode->Left();
}

Problems with in-order tree traversal

I found this picture at Wikipedia:
According to the text underneath the picutre, the in-order is: A, B, C, D, E, F, G, H, I
I understand the order A-F, but what I don't understand is the order of the last three nodes. Is it not supposed to be H, I, G? I thought you were supposed to list internal nodes on your 2nd encounter and leaves on your 1st?
EDIT: would my order be correct if the tree in the picture was a general tree and not a binary tree? (So that G would have only one node, instead of a right node and a null left node.)
Unfortunately Wikipedia is right!
Detailed Algorithm
inorder_traversal(node)
{
if(node!=NULL)
{
inorder_traversas(node->left);
print(node->data); //you overlooked this statement on reaching G
inorder_traversal(node->right);
}
}
Where are you missing the point/going the wrong way?
On reaching G from F, you tried to enter into left child of G which is NULL (in short inorder_traversal(node->left) statement failed as the algorithm has checked if(node!=NULL)
and G's left child is NULL, so after that it again comes back to G, after that you overlooked the print statement which would have printed G there - after overlooking that print statement you jumped to the next statement which checked if node->right is NULL or not - you found it not null (which took you to I). Then you didn't print I directly and checked for its left child. As left child H was there, so you printed it and then you returned to I - the parent, its right child was null. Hence wikipedia is right.
Tip for testing inorder traversal
: Make a binary tree which accepts integer (or numbers generally) as input data, when you'll traverse in inorder fashion, digits will be printed in ascending order.
The answer that has been mentioned by wikipedia is correct. You said you understood A-F, so I will get from G.
In-order traversal follows the series: left-parent-right. So when F is traversed and the algorithm goes to the right child of F, it first encounters G and then tries to locate the left child of G. Since the left child of G is null, nothing is printed and then it goes to the parent, that is, G; so G is printed. Now, it goes to the right child of G which is I. Now, it locates the left child of I which is H and prints it. Then it returns to the parent that is I and prints it. It then traverses to the right of I and finds that it is null. Now as all the nodes have been traversed, the algorithm terminates. Thus the order is G H I
In-order traversal works like this:
1- print the left sub tree.
2- print the root.
3- print the right sub tree
In the example you provided:
1- the left sub-tree of `G` is empty, so we do not print any thing.
2- we print the node `G`.
3- finally we print the right sub-tree of `G`

Exercise review Trees binary c++

Given a binary tree, whose root is located a treasure, and whose internal nodes can contain a dragon or does not contain anything, you are asked to design an algorithm that tells us the leaf of the tree whose path to the root has the lowest number of dragons. In the event that there are multiple paths with the same number of dragons, the algorithm will return that which is more to the left of all them. To do this, implement a function which gets a binary tree whose nodes store integers:
The root contains the integer 0, which represents the treasure.
The internal nodes contain the integer 1 to indicate that the node there is a dragon or the integer 2 to indicate that there is no dragon.
In each leaf stores an integer greater than or equal to 3 that cannot be repeated. and return the whole sheet to the path selected. The tree has at least one root node and a leaf node different from the root. For example, given the following tree (the second test case shown in the example), the algorithm return the integer 4.
I can not upload a picture of the tree of example, but someone tell me with words that I can do to go through all the branches, and to know which is the path with less dragons I'd appreciate it.
A greeting!
You want to think about these problems recursively: if you're at a parent node with...
no children you must have no dragon and a node counter, and you consider yourself to have 0 dragons and be the best node: you'd tell your parent that if asked
a left branch and/or a right branch, then you ask your children for their dragon-count and which node they consider best, and IF the left node reports a lesser or equal dragon count...
you take your best-node and dragon-count from it, ELSE
you take your best-node and dragon-count from the right node
then you add 1 to the dragon-count if your node's storing the integer 1
By starting that processing at the root node, you get the result for the entire tree.
This is the first algorithm that comes to mind. Assuming that you have an array that stores the values in nodes node_value[NODE_NUM], where NODE_NUM is the number of nodes in your tree, and you store index of childs of each node with the arrays left[NODE_NUM] and right[NODE_NUM], and your root will have index root_index. We will store information about the number of dragons in the path to root in the array dragon[NODE_NUM] So the algorithm pseudocode is:
# the recursive function itself
process(node_index):
n_left <- 0
if node_value[left[node_index]] = 1
n_left <- 1
n_right <- 0
if node_value[right[node_index]] = 1
n_right <- 1
dragon[left[node_index]] <- dragon[node_index] + n_left
dragon[right[node_index]] <- dragon[node_index] + n_right
process(left[node_index])
process(right[node_index])
# the number of dragons in path from root to root is, obviously, zero:
dragon[root_index] <- 0
# Call the function itself
process(root_index)
After that, in dragon we will have the number of dragons in the way to root from every nodes in tree. Now, all you have to do is to loop through all nodes and find the node that is a leaf and that its values is minimal:
min <- infinity
node_min <- unknown
for each node:
if node_value[node] >= 3:
if dragon[node] < min:
min <- dragon[node]
node_min <- node
return node_min
Now, the node_min is the node that has least dragons in the path to root.

Alpha-beta pruning - how this code implement resetting variables alpha and beta?

Hello,
I'm trying to understand the alpha beta pruning algorithm using chess as an example from the following code:
def minimax(position, depth):
"""Returns a tuple (score, bestmove) for the position at the given depth"""
if depth == 0 or position.is_checkmate() or position.is_draw():
return (position.evaluate(), None)
else:
if position.to_move == "white":
bestscore = -float("inf")
bestmove = None
for move in position.legal_moves():
new_position = position.make_move(move)
score, move = minimax(new_position, depth - 1)
if score > bestscore: # white maximizes her score
bestscore = score
bestmove = move
return (bestscore, bestmove)
else:
bestscore = float("inf")
bestmove = None
for move in position.legal_moves():
new_position = position.make_move(move)
score, move = minimax(new_position, depth - 1)
if score < bestscore: # black minimizes his score
bestscore = score
bestmove = move
return (bestscore, bestmove)
Here's the link to the blog I got it from: LINK (you can view the code from the link if you like highlighted syntax)
What I don't understand is that in alpha beta pruning the value of alpha and beta variable must change sometimes when you go higher up in a tree. I attached a picture explaining my problem - while I understand the steps 1), 2) and 3), I don't get the 4) step. I know that the 4) step should look like on the picture but I don't know what's going on in the code at that step that the values change.
I followed the code carefully but for some reason I ended up with a = 5 and b = 5 in the 4) step which is ridiculous because that would mean that the branch on the right would get removed which is obviously wrong.
I think your reasoning in your comments is not correct. From your comments, your implicitly believe the search goes to the right branch of tree then back to the left branch of the tree, which is of course incorrect.
Your logic is wrong because at the (5) non-leaf node on the left branch of the tree, the search has only visited the nodes underneath (the leaf nodes (5) and (4). It has not visited the nodes on the right branch of the tree and therefore has no idea what the value will be. Therefore your comment
"there is a max node (a square) and the choice is made between 5 (on the left) and 4 (on the right). And a MAX node above them wants a bigger value so I think alpha should be set to 5 which is a lower bound." is not correct.
It's wrong because only the root node (the max node) knows the value 4 on the right, but it can only be done AFTER step 4. In fact, it can only be done at the end of the search, after all the nodes in the right branch of the tree are visited.

AVL tree balance

I have implemented an AVL tree, but I have a problem.
Suppose I have following tree:
And after adding another node:
Now I must rotate node5 to left:
But after rotation, it is still unbalanced.
Where am I making a mistake?
The presented scenario conforms to the Right-Left case from this Wikipedia description.
Your mistake is that you rotate the imbalanced node (5) at once, without first performing a rotation of its sub-tree.
In general having P as the unbalanced node, L as its left sub-tree and R as its right sub-tree the following rules should be followed at insertion:
balance(N) = Depth(Nleft) - Depth(Nright)
if (balance(P) > 1) // P is node 5 in this scenario
{
if (balance(L) < 0)
{
rotate_left(L);
}
rotate_right(P);
}
else if (balance(P) < -1) // P is node 5 in this scenario
{
if (balance(R) > 0) // R is node 11 in this scenario
{
rotate_right(R); // This case conforms to this scenario
}
rotate_left(P); // ... and of course this, after the above
}
So sometimes two rotations need to be performed, and sometimes only one.
This is nicely visualized at Wikipedia:
The top row shows situations when two rotations are needed. The middle row presents possible scenarios when one rotation is sufficient. Additional rotations transform any top-row scenario to the middle-row scenario.
In particular, for this tree:
After 7 is added:
The balance of 5 is 2. This conforms to the scenario marked with a comment above in the pseudo-code and also to the top-row scenario (the one on the right) in the Wikipedia picture. So before 5 is left-rotated, its right sub-tree 11 needs to be right-rotated:
And it becomes:
Only now, it's the simple case (middle-row right scenario in the Wikipedia picture) to restore balance at 5 by one left-rotation:
And the tree becomes balanced again:
Let me try to analyse more comprehensively,
For a binary tree to be avl tree, the height difference of each node from any left-most leaf to any right-most leaf must lie within {-1, 0, 1}.
Insertion for AVL:
There are four cases for AVL insertion-
L - L
L - R
R - R
R - L
Now,
case (a). [if balance > 1] L-L(left - left) A node X violates the {-1, 0, 1} constraint and
has left height more than right - gives first left of L
has a left sub child Y whose left height is greater than right .. again L
Action - Rotate about Y clockwise. ie. one right rotation.
case (b) (L -R case)Suppose some Z node is to be inserted, for Z, it is first evaluated, at which leaf, left or right, it is placed. Right, if more weight, Left if less weight.
say, Z', new node, wt(Z') > wt(Z), ie, Z' is inserted as right child of Z, case of L - R, the whole link ZZ' is rotated anti clockwise, now it is L-L case and hence solved using above case (a). ie. one Left and then one right rotation.
case (c) [if balance < -1] (R - R case) Similarly, R - R case, simply apply the binary search rule for adjustments and this case works. ie. one left rotation.
case (d) (R - L case) It is first converted to R-R case and hence solved using above case (c). ie. one right and then one left rotation.

Resources