1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
// Applies rules 1-5 to check the balance of a tree with newly inserted
// node n.
void checkBalance(Node *n)
{
Node* G = getGrandparent(n);
if (n == mRoot) {
// case 1: n is the root
n->mIsRed = false; // root cannot be red, must be changed to black
}
// handle additional insert cases here.
// Note that each case is mutually exclusive, EXCEPT THAT case 4 must flow
// into case 5.
// Also note that case 3 recursively examines the grandparent after the
// recoloring.
else if (n->mParent->mIsRed == false) // parent P is black
// case 2: arbitrary parent node is black and one of it's black NIL children was replaced by a red node after
// insert(). Red node n also has two black NIL children.
return; // new node already assigned to be red in insert()
else { // P is red for all remaining cases
if (getUncle(n) != NULL && getUncle(n)->mIsRed == true) { // uncle exist and is red
// Case 3: n's uncle is red, so we need to recolor n's parent and uncle to be black and n's grandparent
// to be black (could potentially violate rule that root cannot be red or that red nodes have two black children. Must
// recursively call "fix" or getBalance procedure.
n->mParent->mIsRed = false; // recolor parent to black
getUncle(n)->mIsRed = false; // recolor uncle to black
getGrandparent(n)->mIsRed = true; // recolor grandparent to be red
checkBalance(getGrandparent(n)); // recursively fix if root is not red or a red node does not have two black children
}
else if (getUncle(n) != NULL) { // uncle exist but is black
// case 4: assume P is left child of G (if not, do the mirror of following procedure). n is left-right or right-left
// grandchild of G. Call single rotate at P (won't fix issue, but allows us to go to case 5).
if ((n->mKey < getGrandparent(n)->mKey) && (n->mKey > n->mParent->mKey)) // parent to left of Grandparent and n to right of parent
singleRotateLeft(n->mParent); // single rotate left @ parent node
else if ((n->mKey > getGrandparent(n)->mKey) && (n->mKey < n->mParent->mKey)) // mirror above process
singleRotateRight(n->mParent); // single rotate right @ parent node
// case 5: Arrive here if cases 1-3 are false. All case 4's will lead into case 5. Need to do a single rotate on G
// and switch colors of P and G: P turns black, G turns red.
if (n->mKey < G->mKey) { // rotate right @ G: fix left-left imbalance after case 4
singleRotateRight(G);
if (n == mRoot) { // can occur coming from case 4
n->mIsRed = false; // recolor root to black
n->mRight->mIsRed = true; // recolor right child to be red
}
else {
n->mParent->mIsRed = false; // recolor parent to black
G->mIsRed = true; // recolor grandparent to be red
}
}
else { // rotate left @ G: fix right-right imbalance after case 4
singleRotateLeft(G);
if (n == mRoot) {
n->mIsRed = false; // recolor root to black
n->mLeft->mIsRed = true; // recolor left child to be red
}
else {
n->mParent->mIsRed = false; // recolor parent to black
G->mIsRed = true; // recolor grandparent to be red
}
}
}
}
}
public:
RedBlackTreeMap() : mRoot(nullptr), mCount(0) {
}
// Return number of elements in the tree
inline int count() const {
return mCount;
}
// Inserts a key/value pair into the tree, updating the red/black balance
// of nodes as necessary. Starts with a normal BST insert, then adjusts.
void insert(const TKey &key, const TValue &value) {
Node* n = new Node(key, value, true); // the node starts red, with null parent, left, and right.
// normal BST insert; n will be placed into its initial position.
// returns false if an existing node was updated (no rebalancing needed)
bool insertedNew = bstInsert(n, mRoot);
if (!insertedNew)
return;
// check cases 1-5 for balance.
checkBalance(n);
}
TValue &find(const TKey &key) {
Node *n = bstFind(key, mRoot);
if (n == nullptr || n == &NIL_NODE)
cout << "Key not found";
else {
if (n == mRoot) cout << "This is a root: ";
else if (n->mRight == &NIL_NODE && n->mLeft == &NIL_NODE)
cout << "This is a leaf: ";
else if ((n->mRight == &NIL_NODE && n->mLeft != &NIL_NODE) || (n->mRight != &NIL_NODE && n->mLeft == &NIL_NODE))
cout << "This node has one child: ";
else if (n->mIsRed == true) cout << "This node is a red: ";
return n->mValue;
}
}
// Returns true if the given key is in the tree.
bool containsKey(const TKey &key) const {
// TODO: using at most three lines of code, finish this method.
// HINT: write the bstFind method first.
if (bstFind(key, mRoot) != nullptr || bstFind(key, mRoot) != &NIL_NODE) return true;
else return false;
}
// Prints a pre-order traversal of the tree's nodes, printing the key, value,
// and color of each node.
void printStructure(Node* n) {
string color;
// TODO: a pre-order traversal. Will need recursion.
if (n == NULL || n == &NIL_NODE)
return;
else {
// first print data of node
if (n->mIsRed == true) color = "Red";
else color = "Black";
cout << n->mKey << " : " << n->mValue << " (" << color << ") " << endl;
// recur on left subtree
printStructure(n->mLeft);
// recur on right subtree
printStructure(n->mRight);
}
}
void printStructureHelper() {
printStructure(mRoot);
}
};
template <typename TK, typename TV>
typename RedBlackTreeMap<TK, TV>::Node RedBlackTreeMap<TK, TV>::NIL_NODE{ TK(), TV(), false };
|