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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
|
#pragma once
#include <vector>
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
template <typename TKey, typename TValue>
class RedBlackTreeMap
{
private:
class Node
{
public:
TKey mKey;
TValue mValue;
Node* mParent;
Node* mLeft;
Node* mRight;
bool mIsRed;
Node(const TKey &key, const TValue &value, bool isRed)
: mKey(key), mValue(value), mIsRed(isRed), mParent(nullptr),
mLeft(&NIL_NODE), mRight(&NIL_NODE)
{
}
};
Node *mRoot;
int mCount;
static Node NIL_NODE;
Node* bstFind(const TKey &key, Node *currentNode)
{
// TODO: write this method. Given a key to find and a node to start at,
// proceed left/right from the current node until finding a node whose
// key is equal to the given key.
// Use < and > to compare the keys of nodes to determine left/right.
return nullptr;
}
Node* getGrandparent(Node *n)
{
// TODO: return the grandparent of n
Node* gp = NULL;
if (n == mRoot) // n is top-most node
return null;
else
return gp = n->mParent->mParent;
}
// Gets the uncle (parent's sibling) of n.
Node* getUncle(Node *n)
{
// TODO: return the uncle of n
Node* p = n->mParent.
* u = NULL;
if (n == mRoot) // base case
return null;
else {
if (p == getGrandparent(n)->mRight) // parent is right child
u = getGrandparent(n)->mLeft; // uncle will be left child
else
u = getGrandparent(n)->mRight; // uncle will be right child
return u;
}
}
// Rotate the tree right at the given node.
void singleRotateRight(Node* n)
{
Node *l = n->mLeft, // prior to rotate, n's child l is to left of n
*lr = l->mRight, // prior to rotate, l's child lr is to right of l
*p = n->mParent; // if n has a parent, l will become its new child after rotate
n->mLeft = lr; // n adopts lr after rotate
lr->mParent = n; // symmetry
l->mRight = n; // n becomes l's right child after rotate
if (p == nullptr) { // no parent above n exist
mRoot = l;
}
else if (p->mLeft == n) { // n used to be its parent's left child
p->mLeft = l;
}
else { // n used to be its parent's right child
p->mRight = l;
}
n->mParent = l; // l becomes n's parent after rotate
l->mParent = p; // l becomes p's child (if p exist) after rotate
}
// Rotate the tree left at the given node.
// Mirror of singleRotateRight
void singleRotateLeft(Node *n)
{
Node* r = n->mRight, // prior to rotate, r's child l is to right of n
* rl = r->mLeft, // prior to rotate, r's child rl is to left of r
* p = n->mParent; // if n has a parent, r will become its new child after rotate
n->mRight = rl;
rl->mParent = n;
r->mLeft = n; // n becomes r's left child after rotate
if (p == nullptr) {
mRoot = r;
}
else if (p->mLeft == n) {
p->mLeft = r;
}
else { // n used to be its parent's right child
p->mRight = r;
}
n->mParent = r;
r->mParent = p;
}
// This method is used by insert. It is complete.
// Inserts the key/value into the BST, and returns true if the key wasn't
// previously in the tree.
bool bstInsert(Node* newNode, Node* currentNode)
{
if (mRoot == nullptr)
{
// case 1
mRoot = newNode;
return true;
}
else
{
if (currentNode->mKey < newNode->mKey)
{
// newNode is larger; go right.
if (currentNode->mRight != &NIL_NODE) // node to right is not a NIL leaf
return bstInsert(newNode, currentNode->mRight); // recursively move down tree
else
{
currentNode->mRight = newNode;
newNode->mParent = currentNode;
mCount++;
return true;
}
}
else if (currentNode->mKey > newNode->mKey)
{
// newNode is smaller; go left.
if (currentNode->mLeft != &NIL_NODE) // node to left is not a NIL leaf
return bstInsert(newNode, currentNode->mLeft); // recursively move down tree
else
{
currentNode->mLeft = newNode;
newNode->mParent = currentNode;
mCount++;
return true;
}
}
else
{
// found a node with the given key; update value.
currentNode->mValue = newNode->mValue;
return false; // did NOT insert a new node.
}
}
}
// Applies rules 1-5 to check the balance of a tree with newly inserted
// node n.
void checkBalance(Node *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.
// case 2: arbitrary node p is black and one of it's black NIL children was replaced by a red node after
// add() value. Red node n also has two black NIL children. Else p is red and so is new child, must go to case 3 to fix
if (n->mParent == !mIsRed) { // parent is black
n->mIsRed = true;
}
else if (n->mParent == mIsRed) { // parent is red
n->mIsRed = true;
// need to go to 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.
checkBalance(n);
}
// case 4: assume parent P is left child of grandparent 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->mParent < n) { // rotate left when n > parent and parent < grandparent
singleRotateLeft(n->mParent)
}
else if (n->mParent > n) { // rotate right when n < parent and parent > grandparent
singleRotateright(n->mParent)
}
// case 5: Arrive here if cases 1-3 are false. All case 4's 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->mParent < n) { // rotate right when n > parent and parent < grandparent: fix left-left imbalance after case 4
Node* p = n->mparent;
singleRotateright(p->mParent);
}
else if (n->mParent > n) { // rotate left when n < parent and parent > grandparent: fix right-right imbalance after case 4
Node* p = n->mparent;
singleRotateleft(p->mParent);
}
}
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)
{
throw std::out_of_range("Key not found");
}
return n->Value;
}
};
template <typename TK, typename TV>
typename RedBlackTreeMap<TK, TV>::Node RedBlackTreeMap<TK, TV>::NIL_NODE{ TK(), TV(), false };
|