I'm coding red-black tree using C++-smart pointers. I designed left and right child to be owned by its parent, so it is unique_ptr<Node> member variable of its parent node, and 'parent' member variable is set to be Node*, because the children clearly does not have ownership.
My problem is: A function that rotates tree takes argument as unique_ptr<Node>&, because it changes positions of node, but I need to pass a parent node to the function, which is in type of Node* that does not match with function argument type.
The solution I think:
1) Make parent() function that returns unique_ptr<Node>. This would be something that starts from the root of the tree and descend to find the pointer that match with parent's address. CONS: slow as hell
2) for node->parent, return node->parent->parent->left or right after some checks.
CONS: code gets little bit messier
3) Make another unique_ptr<Node> by giving Node* as an argument of constructor, and pass that. CONS: I'm not sure that the newly created unique_ptr object would coincide with already existing unique_ptr<Node> object.
4) Use shared_ptr<Node> for child, weak_ptr<Node> for parent instead. Then I could use node->parent.lock() as function argument.
CONS: child pointers' ownership are not meant to be 'shared'.
5) Discard all smart pointer things. Aww.
struct Node {
T data;
Node* parent = nullptr;
unique_ptr<Node> left, right;
bool color = false;
Node(const T& d) : data(d) {};
Node* grandparent() {
if (parent == nullptr) {
return nullptr;
}
return parent->parent;
}
}
/* SOMETHING... */
void rotate_right(unique_ptr<Node>& pivot) {
if (pivot->left == nullptr) {
return;
}
auto pivot_new = move(pivot->left);
auto pivot_parent = pivot->parent;
pivot->left = move(pivot_new->right);
pivot_new->right = move(pivot);
pivot->parent = pivot_new.get();
if (pivot->left != nullptr) {
pivot->left->parent = pivot.get();
}
if (pivot_parent != nullptr) {
if (pivot == pivot_parent->left) {
pivot_parent->left = move(pivot_new);
} else if (pivot == pivot_parent->right) {
pivot_parent->right = move(pivot_new);
} else {
throw invalid_argument("pivot_parent is actually not parent, why?\n");
}
}
pivot_new->parent = pivot_parent;
}
/* SOME OTHER THINGS */
void insert_repair_tree(Node* node) {
if (node->parent == nullptr) {
// this is root. paint BLACK
node->color = true;
} else if (node->parent->color) {
// parent is BLACK. tree is valid. do nothing
} else if (!get_color(node->uncle())) {
// parent is RED. uncle exists and RED. grandpa exists and BLACK.
// repaint parent and uncle to BLACK, and grandpa to RED.
// recursively repair tree at grandpa
node->parent->color = true;
node->uncle()->color = true;
node->grandparent()->color = false;
insert_repair_tree(node->grandparent());
} else {
// parent is RED. uncle is BLACK (not sure that it exists). grandpa exists and BLACK.
auto grandpa = node->grandparent();
if (node == grandpa->left->right) {
rotate_left(grandpa->left);
node = node->left.get();
} else if (node == grandpa->right->left) {
rotate_right(grandpa->right);
node = node->right.get();
}
grandpa = node->grandparent();
// PROBLEMS HERE
if (node == node->parent->left) {
rotate_right(grandpa);
} else if (node == node->parent->right) {
rotate_left(grandpa);
} else {
throw invalid_argument("");
}
node->parent->color = true;
grandpa->color = false;
}
}
Related
I'm trying to create a generic menu class that will be used with a 4 line LCD.
I have a specific (non template) version working, but want to extend it to allow the menu to modify a variety of data types (int, float, unsigned...).
Here's the non template version that's working as expected...
/*
* ideally this design allows for defining an arbitrary menu as shown below
* example...
* root1
* sub1-1
* sub1-2
* root 2
* root 3
* sub3-1
* sub3-2
* sub3-2-1
* sub3-2-2
*
* each node in the menu can be executed, and allow for moving to the next/prev sibling or child/parent
* this flexibility requires that each node contains pointers to parent, child, and sibling nodes.
*/
class MenuNode
{
private:
char *prompt;
int value;
public:
MenuNode *parent=NULL;
MenuNode *child=NULL;
MenuNode *prevSibling=NULL;
MenuNode *nextSibling=NULL;
void SetValue(int value)
{
this->value = value;
}
int GetValue()
{
return value;
}
char *Prompt()
{
return prompt;
}
MenuNode(char *prompt, int initialValue, MenuNode *parent, MenuNode *prevSibling)
{
Serial.print(prompt);Serial.println(F(" MenuNode"));
this->prompt = prompt;
if (prevSibling != NULL)
{
this->prevSibling = prevSibling;
prevSibling->SetNextSibling(this);
this->parent = prevSibling->parent;
}
// prevSibling if provided sets the parent
if (prevSibling==NULL && parent != NULL)
{
this->parent = parent;
this->parent->SetChild(this);
}
value = initialValue;
}
void SetChild(MenuNode *child)
{
Serial.print(prompt);Serial.println(F(" SetChild"));
this->child = child;
}
void SetNextSibling(MenuNode *nextSibling)
{
Serial.print(prompt);Serial.println(F(" SetNextSibling"));
this->nextSibling = nextSibling;
}
};
Here's some test code that creates the menu structure...
// Test menu...
MenuNode r1("R1",10,NULL,NULL);
MenuNode r2("R2",20,NULL,&r1);
MenuNode r21("R21",30,&r2,NULL);
MenuNode r22("R22",40,&r2,&r21); // setting parent is optional, the parent will be set by the prev sibling parent
MenuNode r221("R221",50,&r22,NULL);
MenuNode r2211("R2211",60,&r221,NULL);
MenuNode r2212("R2212",70,NULL,&r2211);
MenuNode r3("R3",30,NULL,&r2);
This code iterates over each element printing out the structure
void PrintMenuStructure(MenuNode *node,int offset)
{
while(node != NULL)
{
for (int i=0;i<offset;i++)
Serial.print("-");
Serial.print(node->Prompt());
Serial.print(" = ");
Serial.print(node->Value());
if (node->parent != NULL)
{
Serial.print(" parent=");
Serial.print(node->parent->Prompt());
}
if (node->prevSibling != NULL)
{
Serial.print(" prevSib=");
Serial.print(node->prevSibling->Prompt());
}
if (node->nextSibling != NULL)
{
Serial.print(" nextSib=");
Serial.print(node->nextSibling->Prompt());
}
if (node->child != NULL)
{
Serial.print(" child=");
Serial.print(node->child->Prompt());
}
Serial.println();
if (node->child != NULL)
PrintMenuStructure(node->child,++offset);
node = node->nextSibling;
}
}
This is the output of the previous function demonstrating the structure of the menu...
R1 = 10 nextSib=R2
R2 = 20 prevSib=R1 nextSib=R3 child=R21
-R21 = 30 parent=R2 nextSib=R22
-R22 = 40 parent=R2 prevSib=R21 child=R221
--R221 = 50 parent=R22 child=R2211
---R2211 = 60 parent=R221 nextSib=R2212
---R2212 = 70 parent=R221 prevSib=R2211
-R3 = 30 prevSib=R2
It all works the way I want, but GetValue/SetValue only operate on int data.
I can create a template version of the class, with the data types of GetValue and SetValue defined by the template parameter, but I don't know now to iterate over the nodes once I do that.
Seems like a simple enough task, but I've been beating my head against the wall for a while, and haven't come up with anything that works. Any help pointing me in the right direction would be appreciated.
I'm trying to figure out how to iterate over a linked list of classes, but can't figure out how to get a pointer to start iterating.
Sorry, I couldn't get the code formatting to work... :(
The way I interpret your requirement: it seems your should make your
int value;
a std::variant.
That's the lowest cost path.
If you templatize the MenuNode class with its value type. Then a MenuNode<int>* cannot be the parent of a MenuNode<float*>, etc. Not without some effort. You'd probably better off make it polymorphic by derivate each type of value your want to support from a common abstract virtual base, and depend on how you want to use the value, design your interface.
I want to insert in the tree but not using any other data structures like queue. I want to insert in level order and no matter what I code, it doesn't. Also I couldn't find any code without queues or things like that.
Here is my attempt;
void insert(int x) {
if (root == NULL) {
root = new node(x, NULL, NULL);
return;
}
node *temp = root;
node *prev = root;
while (temp != NULL) {
if (temp->left != NULL) {
prev = temp;
temp = temp->left;
} else if (temp->right != NULL) {
prev = temp;
temp = temp->right;
}
}
if (temp->left == NULL)
prev->left = new node(x, NULL, NULL);
else if (temp->right == NULL)
prev->right = new node(x, NULL, NULL);
}
I don't have a link for recursive insertion but it should work like this:
bool recursion(node * current_node, node * to_insert, int &max_depth, int cur_depth) {
if(max_depth < cur_depth) {
max_depth = cur_depth;
}
for (auto & current_child : {current_node->left, current_node->right})
if(current_child == NULL) {
if( max_depth > cur_depth ) {
current_child -> left = to_insert;
return true;
}
} else {
if(recursion(current_child, to_insert, max_depth, cur_depth + 1)) {
return true;
}
}
return false;
}
This does depth-first-search (not breadth-first, I was mistaken above, they are very similar in trees) from left to right. So we will first find the left-most leaf, then the one right next to it and so on. We will always track how deep we are in the tree. If at one point we find a node on the second deepest layer that hasn't got a child, it will add the node we want to insert at this point and recurse up the tree. Due to the order in which we traverse the tree, this will find the left most open spot, so exactly what you want.
This method can return false if the submost layer of the tree is full. Then we have to go down to the left-most leaf and insert the node at its left child. One can also save this leaf somehow when we first find it, but that seemed more complicate to me then just searching it again (this can be done without problem in a for-loop).
You can replace the recursive method by an iteration with a stack (there are many sources on the internet explaining how to make a recursive depth-first-search to a iterative one).
I don't really like the in-out-parameter max_depth but it was the easiest to do this.
I've written a Red-Black Tree implementation, with built-in in-order traversal (using nested class Iterator).
I am looking for an (iterative, if possible) algorithm that prints the binary tree graphically using in-order traversal.
Printing orientation isn't relevant, i.e. the tree in the command-line output can be oriented (formatted) like this:
2
/ \
1 4
/ \
3 5
or like this:
|1
|
|
2
| |3
| |
|4
|
|5
or even upside-down, but the tree should be printed using in-oder traversal, using methods provided below:
void Iteraor::first(); // Traverses to the first node.
void Iterator::next(); // Traverses to the next node.
void Iterator::last(); // Traverses to the last node.
so it's possible so make something like this:
RBTree tree;
/* Tree init. */
Iterator from(&tree), until(&tree);
from.first();
until.last();
for (Iterator i = from; i != until; i.next()) {
// PRINTING.
}
This is the original code:
/** A program for Red-Black Tree manipulation: insertion and value retrieval.
* All position relations (first, last, previous, next) are in-order.
*/
class RBTree {
struct Node {
enum class Colour : bool { RED, BLACK };
int value;
Node *left, *right, *parent;
Colour colour;
public:
/* ... */
};
class Iterator {
class Stack {
/* ... */
};
Stack stack;
const RBTree* const tree; // Once set, neither the reference nor the referenced object's attributes can be modified.
Node* pointer;
public:
Iterator(const RBTree*);
void first();
void next();
void last();
/* ... */
Node* getNode() const;
bool operator != (const Iterator&) const;
};
Node *root;
Iterator iterator;
public:
RBTree() : root(nullptr), iterator(this) {}
/* ... */
bool printTree() const;
~RBTree() { deleteTree(); }
};
// TREE // public: //
/* ... */
bool RBTree::printTree() const {
if (root != nullptr) {
// print ??
return true;
}
else
return false;
}
// NODE: Ensures the proper connection. //
void RBTree::Node::setLeft(Node *p_left) {
left = p_left;
if (p_left != nullptr)
p_left->parent = this;
}
void RBTree::Node::setRight(Node *p_right) {
right = p_right;
if (p_right != nullptr)
p_right->parent = this;
}
// ITERATOR //
RBTree::Iterator::Iterator(const RBTree* p_tree) : tree(p_tree), pointer(p_tree->root) {}
// Traverses to the first node (leftmost).
void RBTree::Iterator::first() {
if (pointer != nullptr) {
while (true) {
if (pointer != nullptr) {
stack.push(pointer);
pointer = pointer->left;
}
else {
pointer = stack.peek();
break;
}
}
}
}
// Traverses to next node in-order.
void RBTree::Iterator::next() {
if (pointer != nullptr) {
if (!stack.isEmpty()) {
pointer = stack.pop();
if (pointer->right != nullptr) {
pointer = pointer->right;
first();
}
}
}
}
// Traverses to the last node (rightmost).
void RBTree::Iterator::last() {
pointer = tree->root;
if (pointer != nullptr)
while (pointer->right != nullptr)
pointer = pointer->right;
stack.clear();
}
/* ... */
RBTree::Node* RBTree::Iterator::getNode() const {
return pointer;
}
bool RBTree::Iterator::operator != (const Iterator& p_iterator) const {
return pointer != p_iterator.pointer ? true : false;
}
I have studied the responses at a similar question, but none of the algorithms utilizes the in-order traversal (and most of them are recursive).
EDIT:
Folowing #nonsensickle's advice, the code is clipped down to bare minimum.
The canonical method for in-order traversal using an iterative algorithm is to maintain a stack (or LIFO queue) of the nodes you need to print. Each loop iteration does one of two things:
If you aren't at a leaf, push the current node onto the stack and move on to its leftmost child.
If you are at a leaf, print it, pop the top node off of the stack, print that, and move on to its rightmost child.
You continue until your stack is empty and you're at a leaf.
The formatting, and the generation of the graphical representation of the internode branches, are obviously up to you. Keep in mind that it will require some extra state variables.
EDIT
What I mean by "some extra state variables" is this.
To provide for pretty-printing, you need to keep track of three things:
What level of the tree your current node-to-print is on (counting from the bottom). This tells you (part of) how far to indent it (or offset it from the edge of your canvas, if you're using a 2D drawing library).
Whether your current node-to-print is a left- or right-child. This tells you (again) how far to indent it from its sibling, and also the orientation of the branch connecting it with its parent.
How many nodes away from "center" your node is. This will also be useful for proper spacing from its (non-sibling) neighbors.
It may be possible to make do with less iteration-to-iteration state, but this works for me.
I have this function for deleting a node in a binary search tree which seems to be working EXCEPT in the case where I ask it to delete the root node. It is supposed to take the right-most value on the left and replace the node with that; however, once that happens, the new root node's children pointers don't seem to point to the original root node's children. Code is as follows:
bool delete_node(Node*& root, TYPE data) {
Node* toDelete;
Node* parent;
// This function is defined appropriately elsewhere, and finds the target to be deleted
toDelete = find(data, root);
if (!toDelete) {
return false;
}
// This function is defined appropriately elsewhere, and finds the parent of the node to be deleted
parent = find_parent(root, toDelete);
// Other cases left out because they work
// If the target node has two children:
if (toDelete->left && toDelete->right)
{
// find rightmost child on left that is a leaf
Node *replacement = toDelete->left;
while (replacement->right)
{
replacement = replacement->right;
}
// set the target node's data
toDelete->data = replacement->data;
if (parent)
{
if ( parent->data < toDelete->data )
{
parent->right = replacement;
} else
{
parent->left = replacement;
}
} else
{
// if node has no parents, then it is the root and should be replaced with replacement
// This line here is what seems to be causing my trouble...I think
root = replacement;
}
parent = find_parent(toDelete, replacement);
if (parent)
{
if (parent->left == replacement)
parent->left = NULL;
else
parent->right = NULL;
}
delete toDelete;
return true;
}
}
Thanks in advance!
what I ended coming up with was this: keep track of the parent node that is one above the node that replaces the node to be deleted. there will then be 2 cases to consider: the parent is the node to be deleted and parent is not the node to be deleted. by replacing the appropriate parts of the tree at the right case, the structure and invariants of the tree remained ok and the node to be deleted was successfully deleted. technically, it would be the data at the node to be deleted.
else if (toDelete->left != NULL && toDelete->right != NULL) {
// find rightmost child on left that is a leaf
Node* replacement = toDelete->left;
parent = toDelete;
// parent is now the parent of the replacement
while ( replacement->right ) {
parent = replacement;
replacement = replacement->right;
} // By the end, parent will be the node one above replacement
toDelete->key = replacement->key;
if (parent == target)
parent->left = replacement->left;
else
parent->right = replacement->left;
delete replacement;
return true;
}
This is what I did to make it work. Just check if the node is the root node, and if so, set the new root. Below is the working code I have. The three places marked by asterisks is what I added to make it work. All the other lines of code is just standard textbook theory.
inline NamesBinaryTree::Node* NamesBinaryTree::Node::removeNode (Node*& node, const Female* female, stringComparisonFunction s) { // Taken from p.253 of Alex Allain's "Jumping Into C++".
if (!node)
return nullptr;
if (node->femaleInfo.first == female->getName()) {
if (!node->left) { // i.e. node has either one child or zero children.
Node* rightNode = node->right;
if (node->isRoot()) // ***
namesBinaryTree.setRoot(rightNode); // Tested to work correctly. Note that we cannot call 'delete node;', since that will delete the very root that we are setting!
else
delete node;
return rightNode; // This will return nullptr if node->right is also nullptr, which is what we would want to do anyway since that would mean that node has zero children.
}
if (!node->right) { // i.e. node has exactly one child, namely its left child, in which case return that left child.
Node* leftNode = node->left;
if (node->isRoot()) // ***
namesBinaryTree.setRoot(leftNode);
else
delete node;
return leftNode; // This will never be nullptr, else the previous if condition would have been met instead.
}
Node* maxNode = findMaxNode(node->left); // node has two children, so it shall be replaced by the largest valued node in its left subtree.
maxNode->left = removeMaxNode(node->left, maxNode); // Note that maxNode->left = node->left is not enough because without actually removing maxNode, the node that was pointing to maxNode will now be pointing to maxNode in its new position (and the subtree under it), and the subtree that was under maxNode will now be gone.
maxNode->right = node->right;
if (node->isRoot()) // ***
namesBinaryTree.setRoot(maxNode); // Tested to work correctly.
else
delete node;
return maxNode;
}
else {
const int result = (*s)(female->getName(), node->femaleInfo.first);
if (result < 0)
node->left = removeNode(node->left, female, s); // This assignment can only work if node is passed by reference (hence the parameter Node*& node), at least this is what "C++ Essentials" did in their solution, p.247.
else // Don't use 'else if (result > 0)'. Let the equality case be covered here too (just as in NamesBinaryTree::Node::insertNode).
node->right = removeNode(node->right, female, s); // Again, this assignment can only work if node is passed by reference (hence the parameter Node*& node).
}
return node; // So if node->femaleInfo.first != female->getName(), then the same node is returned, which means that the two assignment lines above don't change any values.
}
Edited*: I'm working on the delete function for a binary search tree. I'm just working on the first case right now. I think this is correct, but I'm wondering if it can be done recursively, or more efficiently. Any help is appreciated. Assume BSTSearch searches for a node, isLeaf returns true if the node is a leaf, and each node has a pointer that allows them access to their parent.
void
BinarySearchTree::BSTDelete(int x, BSTNode *node){
BSTNode *deleteNode;
deleteNode = BSTSearch(x,node);
if(isLeaf(deleteNode)){
if(deleteNode->sortkey > (deleteNode->parent)->sortkey){
delete (deleteNode->parent)->right;
(deleteNode->parent)->right = NULL;
}
else{
delete (deleteNode->parent)->left;
(deleteNode->parent)->left = NULL;
}
}
You don't need a pointer to the parent. Here is a recursive version that should work: (pass by reference (&), in case you don't know, allows you to modify the variable, similar to pass by pointer; BSTNode *& is a pointer passed by reference, so we can modify the value of node->left/right (pointers) (not just what they're pointing to))
void BinarySearchTree::BSTDelete(int x, BSTNode *&node)
{
if (node == NULL)
return;
if (x == node->sortKey)
{
if (isLeaf(node))
{
delete node;
node = NULL;
}
else
{
// other stuff goes here
}
return;
}
else if (x < node->sortKey)
BSTDelete(x, node->left);
else
BSTDelete(x, node->right);
}