boggle/lib/StanfordCPPLib/map.h
2024-09-11 17:33:32 +02:00

920 lines
25 KiB
C++
Executable File

/*
* File: map.h
* -----------
* This file exports the template class <code>Map</code>, which
* maintains a collection of <i>key</i>-<i>value</i> pairs.
*/
#ifndef _map_h
#define _map_h
#include <cstdlib>
#include "foreach.h"
#include "stack.h"
#include "vector.h"
/*
* Class: Map<KeyType,ValueType>
* -----------------------------
* This class maintains an association between <b><i>keys</i></b> and
* <b><i>values</i></b>. The types used for keys and values are
* specified using templates, which makes it possible to use
* this structure with any data type.
*/
template <typename KeyType, typename ValueType>
class Map {
public:
/*
* Constructor: Map
* Usage: Map<KeyType,ValueType> map;
* ----------------------------------
* Initializes a new empty map that associates keys and values of the
* specified types.
*/
Map();
/*
* Destructor: ~Map
* ----------------
* Frees any heap storage associated with this map.
*/
virtual ~Map();
/*
* Method: size
* Usage: int nEntries = map.size();
* ---------------------------------
* Returns the number of entries in this map.
*/
int size() const;
/*
* Method: isEmpty
* Usage: if (map.isEmpty()) ...
* -----------------------------
* Returns <code>true</code> if this map contains no entries.
*/
bool isEmpty() const;
/*
* Method: put
* Usage: map.put(key, value);
* ---------------------------
* Associates <code>key</code> with <code>value</code> in this map.
* Any previous value associated with <code>key</code> is replaced
* by the new value.
*/
void put(const KeyType & key, const ValueType & value);
/*
* Method: get
* Usage: ValueType value = map.get(key);
* --------------------------------------
* Returns the value associated with <code>key</code> in this map.
* If <code>key</code> is not found, <code>get</code> returns the
* default value for <code>ValueType</code>.
*/
ValueType get(const KeyType & key) const;
/*
* Method: containsKey
* Usage: if (map.containsKey(key)) ...
* ------------------------------------
* Returns <code>true</code> if there is an entry for <code>key</code>
* in this map.
*/
bool containsKey(const KeyType & key) const;
/*
* Method: remove
* Usage: map.remove(key);
* -----------------------
* Removes any entry for <code>key</code> from this map.
*/
void remove(const KeyType & key);
/*
* Method: clear
* Usage: map.clear();
* -------------------
* Removes all entries from this map.
*/
void clear();
/*
* Method: keys
* Usage: Vector<KeyType> keys = map.keys();
* -------------------------------------------
* Returns a collection containing all keys in this map.
*/
Vector<KeyType> keys() const;
/*
* Method: values
* Usage: Vector<ValueType> values = map.values();
* -------------------------------------------
* Returns a collection containing all values in this map.
*/
Vector<ValueType> values() const;
/*
* Operator: []
* Usage: map[key]
* ---------------
* Selects the value associated with <code>key</code>. This syntax
* makes it easy to think of a map as an "associative array"
* indexed by the key type. If <code>key</code> is already present
* in the map, this function returns a reference to its associated
* value. If key is not present in the map, a new entry is created
* whose value is set to the default for the value type.
*/
ValueType & operator[](const KeyType & key);
ValueType operator[](const KeyType & key) const;
/*
* Method: toString
* Usage: string str = map.toString();
* -----------------------------------
* Converts the map to a printable string representation.
*/
std::string toString();
/*
* Method: mapAll
* Usage: map.mapAll(fn);
* ----------------------
* Iterates through the map entries and calls <code>fn(key, value)</code>
* for each one. The keys are processed in ascending order, as defined
* by the comparison function.
*/
void mapAll(void (*fn)(KeyType, ValueType)) const;
void mapAll(void (*fn)(const KeyType &, const ValueType &)) const;
template <typename FunctorType>
void mapAll(FunctorType fn) const;
/*
* Additional Map operations
* -------------------------
* In addition to the methods listed in this interface, the Map
* class supports the following operations:
*
* - Stream I/O using the << and >> operators
* - Deep copying for the copy constructor and assignment operator
* - Iteration using the range-based for statement and STL iterators
*
* All iteration is guaranteed to proceed in the order established by
* the comparison function passed to the constructor, which ordinarily
* matches the order of the key type.
*/
/* Private section */
/**********************************************************************/
/* Note: Everything below this point in the file is logically part */
/* of the implementation and should not be of interest to clients. */
/**********************************************************************/
/*
* Implementation notes:
* ---------------------
* The map class is represented using a binary search tree. The
* specific implementation used here is the classic AVL algorithm
* developed by Georgii Adel'son-Vel'skii and Evgenii Landis in 1962.
*/
private:
/* Constant definitions */
static const int BST_LEFT_HEAVY = -1;
static const int BST_IN_BALANCE = 0;
static const int BST_RIGHT_HEAVY = +1;
/* Type definition for nodes in the binary search tree */
struct BSTNode {
KeyType key; /* The key stored in this node */
ValueType value; /* The corresponding value */
BSTNode *left; /* Subtree containing all smaller keys */
BSTNode *right; /* Subtree containing all larger keys */
int bf; /* AVL balance factor */
};
/*
* Implementation notes: Comparator
* --------------------------------
* The Comparator class encapsulates a functor that compares two values
* of KeyType. In contrast to the classes in the STL, all of which embed
* the comparator in the type, the Map class and its derivatives pass an
* optional Comparator value. While this strategy results in a more
* complex implementation, it has the advantage of allowing maps and sets
* to carry their own comparators without forcing the client to include
* the comparator in the template declaration. This simplification is
* particularly important for the Graph class.
*
* The allocation is required in the TemplateComparator class because
* the type std::binary_function has subclasses but does not define a
* virtual destructor.
*/
class Comparator {
public:
virtual ~Comparator() { }
virtual bool lessThan(const KeyType & k1, const KeyType & k2) = 0;
virtual Comparator *clone() = 0;
};
template <typename CompareType>
class TemplateComparator : public Comparator {
public:
TemplateComparator(CompareType cmp) {
this->cmp = new CompareType(cmp);
}
virtual bool lessThan(const KeyType & k1, const KeyType & k2) {
return (*cmp)(k1, k2);
}
virtual Comparator *clone() {
return new TemplateComparator<CompareType>(*cmp);
}
private:
CompareType *cmp;
};
Comparator & getComparator() const {
return *cmpp;
}
/* Instance variables */
BSTNode *root; /* Pointer to the root of the tree */
int nodeCount; /* Number of entries in the map */
Comparator *cmpp; /* Pointer to the comparator */
int (*cmpFn)(const KeyType &, const KeyType &);
/* Private methods */
/*
* Implementation notes: findNode(t, key)
* --------------------------------------
* Searches the tree rooted at t to find the specified key, searching
* in the left or right subtree, as approriate. If a matching node
* is found, findNode returns a pointer to the value cell in that node.
* If no matching node exists in the tree, findNode returns NULL.
*/
ValueType *findNode(BSTNode *t, const KeyType & key) const {
if (t == NULL) return NULL;
int sign = compareKeys(key, t->key);
if (sign == 0) return &t->value;
if (sign < 0) {
return findNode(t->left, key);
} else {
return findNode(t->right, key);
}
}
/*
* Implementation notes: addNode(t, key, heightFlag)
* -------------------------------------------------
* Searches the tree rooted at t to find the specified key, searching
* in the left or right subtree, as approriate. If a matching node
* is found, addNode returns a pointer to the value cell in that node,
* just like findNode. If no matching node exists in the tree, addNode
* creates a new node with a default value. The heightFlag reference
* parameter returns a bool indicating whether the height of the tree
* was changed by this operation.
*/
ValueType *addNode(BSTNode * & t, const KeyType & key, bool & heightFlag) {
heightFlag = false;
if (t == NULL) {
t = new BSTNode();
t->key = key;
t->value = ValueType();
t->bf = BST_IN_BALANCE;
t->left = t->right = NULL;
heightFlag = true;
nodeCount++;
return &t->value;
}
int sign = compareKeys(key, t->key);
if (sign == 0) return &t->value;
ValueType *vp = NULL;
int bfDelta = BST_IN_BALANCE;
if (sign < 0) {
vp = addNode(t->left, key, heightFlag);
if (heightFlag) bfDelta = BST_LEFT_HEAVY;
} else {
vp = addNode(t->right, key, heightFlag);
if (heightFlag) bfDelta = BST_RIGHT_HEAVY;
}
updateBF(t, bfDelta);
heightFlag = (bfDelta != 0 && t->bf != BST_IN_BALANCE);
return vp;
}
/*
* Implementation notes: removeNode(t, key)
* ----------------------------------------
* Removes the node containing the specified key from the tree rooted
* at t. The return value is true if the height of this subtree
* changes. The removeTargetNode method does the actual deletion.
*/
bool removeNode(BSTNode * & t, const KeyType & key) {
if (t == NULL) return false;
int sign = compareKeys(key, t->key);
if (sign == 0) return removeTargetNode(t);
int bfDelta = BST_IN_BALANCE;
if (sign < 0) {
if (removeNode(t->left, key)) bfDelta = BST_RIGHT_HEAVY;
} else {
if (removeNode(t->right, key)) bfDelta = BST_LEFT_HEAVY;
}
updateBF(t, bfDelta);
return bfDelta != 0 && t->bf == BST_IN_BALANCE;
}
/*
* Implementation notes: removeTargetNode(t)
* -----------------------------------------
* Removes the node which is passed by reference as t. The easy case
* occurs when either (or both) of the children is NULL; all you need
* to do is replace the node with its non-NULL child, if any. If both
* children are non-NULL, this code finds the rightmost descendent of
* the left child; this node may not be a leaf, but will have no right
* child. Its left child replaces it in the tree, after which the
* replacement data is moved to the position occupied by the target node.
*/
bool removeTargetNode(BSTNode * & t) {
BSTNode *toDelete = t;
if (t->left == NULL) {
t = t->right;
delete toDelete;
nodeCount--;
return true;
} else if (t->right == NULL) {
t = t->left;
delete toDelete;
nodeCount--;
return true;
} else {
BSTNode *successor = t->left;
while (successor->right != NULL) {
successor = successor->right;
}
t->key = successor->key;
t->value = successor->value;
if (removeNode(t->left, successor->key)) {
updateBF(t, BST_RIGHT_HEAVY);
return (t->bf == BST_IN_BALANCE);
}
return false;
}
}
/*
* Implementation notes: updateBF(t, bfDelta)
* ------------------------------------------
* Updates the balance factor in the node and rebalances the tree
* if necessary.
*/
void updateBF(BSTNode * & t, int bfDelta) {
t->bf += bfDelta;
if (t->bf < BST_LEFT_HEAVY) {
fixLeftImbalance(t);
} else if (t->bf > BST_RIGHT_HEAVY) {
fixRightImbalance(t);
}
}
/*
* Implementation notes: fixLeftImbalance(t)
* -----------------------------------------
* This function is called when a node has been found that is out
* of balance with the longer subtree on the left. Depending on
* the balance factor of the left child, the code performs a
* single or double rotation.
*/
void fixLeftImbalance(BSTNode * & t) {
BSTNode *child = t->left;
if (child->bf == BST_RIGHT_HEAVY) {
int oldBF = child->right->bf;
rotateLeft(t->left);
rotateRight(t);
t->bf = BST_IN_BALANCE;
switch (oldBF) {
case BST_LEFT_HEAVY:
t->left->bf = BST_IN_BALANCE;
t->right->bf = BST_RIGHT_HEAVY;
break;
case BST_IN_BALANCE:
t->left->bf = t->right->bf = BST_IN_BALANCE;
break;
case BST_RIGHT_HEAVY:
t->left->bf = BST_LEFT_HEAVY;
t->right->bf = BST_IN_BALANCE;
break;
}
} else if (child->bf == BST_IN_BALANCE) {
rotateRight(t);
t->bf = BST_RIGHT_HEAVY;
t->right->bf = BST_LEFT_HEAVY;
} else {
rotateRight(t);
t->right->bf = t->bf = BST_IN_BALANCE;
}
}
/*
* Implementation notes: rotateLeft(t)
* -----------------------------------
* This function performs a single left rotation of the tree
* that is passed by reference. The balance factors
* are unchanged by this function and must be corrected at a
* higher level of the algorithm.
*/
void rotateLeft(BSTNode * & t) {
BSTNode *child = t->right;
t->right = child->left;
child->left = t;
t = child;
}
/*
* Implementation notes: fixRightImbalance(t)
* ------------------------------------------
* This function is called when a node has been found that
* is out of balance with the longer subtree on the right.
* Depending on the balance factor of the right child, the
* code performs a single or double rotation.
*/
void fixRightImbalance(BSTNode * & t) {
BSTNode *child = t->right;
if (child->bf == BST_LEFT_HEAVY) {
int oldBF = child->left->bf;
rotateRight(t->right);
rotateLeft(t);
t->bf = BST_IN_BALANCE;
switch (oldBF) {
case BST_LEFT_HEAVY:
t->left->bf = BST_IN_BALANCE;
t->right->bf = BST_RIGHT_HEAVY;
break;
case BST_IN_BALANCE:
t->left->bf = t->right->bf = BST_IN_BALANCE;
break;
case BST_RIGHT_HEAVY:
t->left->bf = BST_LEFT_HEAVY;
t->right->bf = BST_IN_BALANCE;
break;
}
} else if (child->bf == BST_IN_BALANCE) {
rotateLeft(t);
t->bf = BST_LEFT_HEAVY;
t->left->bf = BST_RIGHT_HEAVY;
} else {
rotateLeft(t);
t->left->bf = t->bf = BST_IN_BALANCE;
}
}
/*
* Implementation notes: rotateRight(t)
* ------------------------------------
* This function performs a single right rotation of the tree
* that is passed by reference. The balance factors
* are unchanged by this function and must be corrected at a
* higher level of the algorithm.
*/
void rotateRight(BSTNode * & t) {
BSTNode *child = t->left;
t->left = child->right;
child->right = t;
t = child;
}
/*
* Implementation notes: deleteTree(t)
* -----------------------------------
* Deletes all the nodes in the tree.
*/
void deleteTree(BSTNode *t) {
if (t != NULL) {
deleteTree(t->left);
deleteTree(t->right);
delete t;
}
}
/*
* Implementation notes: mapAll
* ----------------------------
* Calls fn(key, value) for every key-value pair in the tree.
*/
void mapAll(BSTNode *t, void (*fn)(KeyType, ValueType)) const {
if (t != NULL) {
mapAll(t->left, fn);
fn(t->key, t->value);
mapAll(t->right, fn);
}
}
void mapAll(BSTNode *t,
void (*fn)(const KeyType &, const ValueType &)) const {
if (t != NULL) {
mapAll(t->left, fn);
fn(t->key, t->value);
mapAll(t->right, fn);
}
}
template <typename FunctorType>
void mapAll(BSTNode *t, FunctorType fn) const {
if (t != NULL) {
mapAll(t->left, fn);
fn(t->key, t->value);
mapAll(t->right, fn);
}
}
void deepCopy(const Map & other) {
root = copyTree(other.root);
nodeCount = other.nodeCount;
cmpp = (other.cmpp == NULL) ? NULL : other.cmpp->clone();
}
BSTNode *copyTree(BSTNode * const t) {
if (t == NULL) return NULL;
BSTNode *np = new BSTNode();
np->key = t->key;
np->value = t->value;
np->bf = t->bf;
np->left = copyTree(t->left);
np->right = copyTree(t->right);
return np;
}
public:
/*
* Hidden features
* ---------------
* The remainder of this file consists of the code required to
* support deep copying and iteration. Including these methods in
* the public portion of the interface would make that interface more
* difficult to understand for the average client.
*/
/* Extended constructors */
template <typename CompareType>
explicit Map(CompareType cmp) {
root = NULL;
nodeCount = 0;
cmpp = new TemplateComparator<CompareType>(cmp);
}
/*
* Implementation notes: compareKeys(k1, k2)
* -----------------------------------------
* Compares the keys k1 and k2 and returns an integer (-1, 0, or +1)
* depending on whether k1 < k2, k1 == k2, or k1 > k2, respectively.
*/
int compareKeys(const KeyType & k1, const KeyType & k2) const {
if (cmpp->lessThan(k1, k2)) return -1;
if (cmpp->lessThan(k2, k1)) return +1;
return 0;
}
/*
* Deep copying support
* --------------------
* This copy constructor and operator= are defined to make a
* deep copy, making it possible to pass/return maps by value
* and assign from one map to another.
*/
Map & operator=(const Map & src) {
if (this != &src) {
clear();
deepCopy(src);
}
return *this;
}
Map(const Map & src) {
deepCopy(src);
}
/*
* Iterator support
* ----------------
* The classes in the StanfordCPPLib collection implement input
* iterators so that they work symmetrically with respect to the
* corresponding STL classes.
*/
class iterator : public std::iterator<std::input_iterator_tag,KeyType> {
private:
struct NodeMarker {
BSTNode *np;
bool processed;
};
const Map *mp; /* Pointer to the map */
int index; /* Index of current element */
Stack<NodeMarker> stack; /* Stack of unprocessed nodes */
void findLeftmostChild() {
BSTNode *np = stack.peek().np;
if (np == NULL) return;
while (np->left != NULL) {
NodeMarker marker = { np->left, false };
stack.push(marker);
np = np->left;
}
}
public:
iterator() {
/* Empty */
}
iterator(const Map *mp, bool end) {
this->mp = mp;
if (end || mp->nodeCount == 0) {
index = mp->nodeCount;
} else {
index = 0;
NodeMarker marker = { mp->root, false };
stack.push(marker);
findLeftmostChild();
}
}
iterator(const iterator & it) {
mp = it.mp;
index = it.index;
stack = it.stack;
}
iterator& operator=(const iterator & it) {
if(*this != it){
mp = it.mp;
index = it.index;
stack = it.stack;
}
return *this;
}
iterator & operator++() {
NodeMarker marker = stack.pop();
BSTNode *np = marker.np;
if (np->right == NULL) {
while (!stack.isEmpty() && stack.peek().processed) {
stack.pop();
}
} else {
marker.processed = true;
stack.push(marker);
marker.np = np->right;
marker.processed = false;
stack.push(marker);
findLeftmostChild();
}
index++;
return *this;
}
iterator operator++(int) {
iterator copy(*this);
operator++();
return copy;
}
bool operator==(const iterator & rhs) {
return mp == rhs.mp && index == rhs.index;
}
bool operator!=(const iterator & rhs) {
return !(*this == rhs);
}
KeyType operator*() {
return stack.peek().np->key;
}
KeyType *operator->() {
return &stack.peek().np->key;
}
friend class Map;
};
iterator begin() const {
return iterator(this, false);
}
iterator end() const {
return iterator(this, true);
}
};
template <typename KeyType, typename ValueType>
Map<KeyType,ValueType>::Map() {
root = NULL;
nodeCount = 0;
cmpp = new TemplateComparator< less<KeyType> >(less<KeyType>());
}
template <typename KeyType, typename ValueType>
Map<KeyType,ValueType>::~Map() {
if (cmpp != NULL) delete cmpp;
deleteTree(root);
}
template <typename KeyType, typename ValueType>
int Map<KeyType,ValueType>::size() const {
return nodeCount;
}
template <typename KeyType, typename ValueType>
bool Map<KeyType,ValueType>::isEmpty() const {
return nodeCount == 0;
}
template <typename KeyType, typename ValueType>
void Map<KeyType,ValueType>::put(const KeyType & key,
const ValueType & value) {
bool dummy;
*addNode(root, key, dummy) = value;
}
template <typename KeyType, typename ValueType>
ValueType Map<KeyType,ValueType>::get(const KeyType & key) const {
ValueType *vp = findNode(root, key);
if (vp == NULL) return ValueType();
return *vp;
}
template <typename KeyType, typename ValueType>
void Map<KeyType,ValueType>::remove(const KeyType & key) {
removeNode(root, key);
}
template <typename KeyType, typename ValueType>
void Map<KeyType,ValueType>::clear() {
deleteTree(root);
root = NULL;
nodeCount = 0;
}
template <typename KeyType, typename ValueType>
bool Map<KeyType,ValueType>::containsKey(const KeyType & key) const {
return findNode(root, key) != NULL;
}
template <typename KeyType, typename ValueType>
ValueType & Map<KeyType,ValueType>::operator[](const KeyType & key) {
bool dummy;
return *addNode(root, key, dummy);
}
template <typename KeyType, typename ValueType>
ValueType Map<KeyType,ValueType>::operator[](const KeyType & key) const {
return get(key);
}
template <typename KeyType, typename ValueType>
void Map<KeyType,ValueType>::mapAll(void (*fn)(KeyType, ValueType)) const {
mapAll(root, fn);
}
template <typename KeyType, typename ValueType>
void Map<KeyType,ValueType>::mapAll(void (*fn)(const KeyType &,
const ValueType &)) const {
mapAll(root, fn);
}
template <typename KeyType, typename ValueType>
template <typename FunctorType>
void Map<KeyType,ValueType>::mapAll(FunctorType fn) const {
mapAll(root, fn);
}
template <typename KeyType, typename ValueType>
std::string Map<KeyType,ValueType>::toString() {
ostringstream os;
os << *this;
return os.str();
}
template <typename KeyType,typename ValueType>
Vector<KeyType> Map<KeyType,ValueType>::keys() const {
Vector<KeyType> keyset;
foreach (KeyType key in *this) {
keyset.add(key);
}
return keyset;
}
template <typename KeyType,typename ValueType>
Vector<ValueType> Map<KeyType,ValueType>::values() const {
Vector<ValueType> values;
foreach (KeyType key in *this) {
values.add(this->get(key));
}
return values;
}
/*
* Implementation notes: << and >>
* -------------------------------
* The insertion and extraction operators use the template facilities in
* strlib.h to read and write generic values in a way that treats strings
* specially.
*/
template <typename KeyType, typename ValueType>
std::ostream & operator<<(std::ostream & os,
const Map<KeyType,ValueType> & map) {
os << "{";
typename Map<KeyType,ValueType>::iterator begin = map.begin();
typename Map<KeyType,ValueType>::iterator end = map.end();
typename Map<KeyType,ValueType>::iterator it = begin;
while (it != end) {
if (it != begin) os << ", ";
writeGenericValue(os, *it, false);
os << ":";
writeGenericValue(os, map[*it], false);
++it;
}
return os << "}";
}
template <typename KeyType, typename ValueType>
std::istream & operator>>(std::istream & is, Map<KeyType,ValueType> & map) {
char ch;
is >> ch;
if (ch != '{') error("operator >>: Missing {");
map.clear();
is >> ch;
if (ch != '}') {
is.unget();
while (true) {
KeyType key;
readGenericValue(is, key);
is >> ch;
if (ch != ':') error("operator >>: Missing colon after key");
ValueType value;
readGenericValue(is, value);
map[key] = value;
is >> ch;
if (ch == '}') break;
if (ch != ',') {
error(std::string("operator >>: Unexpected character ") + ch);
}
}
}
return is;
}
#endif