有了上一次的经验,这次再来写模板类+迭代器确实是更加得心应手了许多,不过这个里面的问题也比上次的复杂了一些,因为涉及到了排序,和左右子树的链接,除此之外也没有什么好说的了,有问题可以留言
bidirectional_map.h (完整版+注释)
#include <iostream>
#include <utility>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
// 节点类
template<class T1, class T2>
class Node {
public:
pair<T1, T2> val;
Node<T2, T1>*link;
Node<T1, T2>*left, *right, *parent;
Node() :link(NULL), left(NULL), right(NULL), parent(NULL) {}
Node(const pair<T1, T2> init) :val(init), link(NULL), left(NULL), right(NULL), parent(NULL) {}
};
// map的迭代器类
template<class T1, class T2>
class map_iterator {
public:
// ptr_ 表示当前节点, root_为辅助节点记录了根节点
Node<T1, T2>*ptr_, *root_;
map_iterator() : ptr_(NULL), root_(NULL) {}
// 根据用于初始化的节点直接找到根节点,就不用多传入一个参数
map_iterator(Node<T1, T2> *init) {
ptr_ = init;
root_ = init;
while (root_!=NULL && root_->parent!=NULL)
root_ = root_->parent;
}
~map_iterator() { ptr_ = NULL; root_ = NULL; }
// 几个基本的符号操作
map_iterator& operator=(const map_iterator& old) { ptr_ = old.ptr_; root_ = old.root_; return *this; }
pair<T1, T2>& operator*() const { return ptr_->val; }
friend bool operator== (const map_iterator& lft, const map_iterator& rgt) {
return lft.ptr_ == rgt.ptr_;
}
friend bool operator!= (const map_iterator& lft, const map_iterator& rgt) {
return lft.ptr_ != rgt.ptr_;
}
// 树形结构没有明显的线性结构,所以需要较为复杂的操作来进行前移和后移
// 画图模拟更直观
// 重载迭代器后移
map_iterator & operator++() {
// 当前节点右孩子的最左端点,就是当前节点的后一个节点
if (ptr_->right != NULL) {
ptr_ = ptr_->right;
while (ptr_->left != NULL) {
ptr_ = ptr_->left;
}
}
// 当前节点没有右孩子,说明已经走到底部,需要往上回溯到这条直链的最左端
// 他的父亲节点就是最右下端点的后一个节点
else {
while (ptr_->parent != NULL && ptr_->parent->right == ptr_) {
ptr_ = ptr_->parent;
}
ptr_ = ptr_->parent;
}
return *this;
}
map_iterator operator++(int) {
map_iterator tmp(*this);
++(*this);
return tmp;
}
// 重载前移操作
map_iterator& operator--() {
// 当前节点不是根节点或空节点、尾节点
if (ptr_ && ptr_->parent) {
// 是叶子节点
if (!(ptr_->right) && !(ptr_->left)) {
// 当前节点为上一个节点的右孩子,说明前一个节点就是上一个
if (ptr_ == ptr_->parent->right)
ptr_ = ptr_->parent;
// 当前节点为上一个节点的左孩子
else if (ptr_ == ptr_->parent->left) {
// 如果这个节点还有左孩子,那这个左孩子是上一个
if (ptr_->left)
ptr_ = ptr_->left;
// 如果没有左孩子了,那前一个节点就是这条直链最左端的父亲节点
else {
while (ptr_->parent != NULL && ptr_->parent->left == ptr_)
ptr_ = ptr_->parent;
ptr_ = ptr_->parent;
}
}
}
// 如果不是叶子节点
else {
// 左孩子的第一个右孩子就是上一个
if (ptr_->left) {
ptr_ = ptr_->left;
while (ptr_->left && !(ptr_->right))
ptr_ = ptr_->left;
if (ptr_->right)
ptr_ = ptr_->right;
}
else if (ptr_->right)
ptr_ = ptr_->parent;
}
}
// 如果是根节点
else if (ptr_ && !(ptr_->parent)) {
// 它的上一个只能是左孩子的最右孩子
if (ptr_->left) {
ptr_ = ptr_->left;
while (ptr_->right)
ptr_ = ptr_->right;
}
}
// 如果是尾节点,那只需要找到这棵树的最后一个节点
else {
ptr_ = root_;
while (ptr_->right)
ptr_ = ptr_->right;
}
return *this;
}
map_iterator operator--(int) {
map_iterator tmp(*this);
--(*this);
return tmp;
}
// 起始节点就是最左节点
map_iterator& begin() {
ptr_ = root_;
while (ptr_&&ptr_->left)
ptr_ = ptr_->left;
return *this;
}
// 尾节点就是一个空节点
map_iterator& end() {
ptr_ = NULL;
return *this;
}
// 链接到隔壁树上的同一个
map_iterator<T2, T1> follow_link() { return map_iterator<T2, T1>(ptr_->link); }
};
// map类
template<class T1, class T2>
class bidirectional_map {
private:
// 键在前的子树
Node<T1, T2>*key_root;
// 值在前的反向子树
Node<T2, T1>*value_root;
// 树的容量
int size_;
public:
// 键迭代器
typedef map_iterator<T1, T2> key_iterator;
friend class map_iterator<T1, T2>;
// 值迭代器
typedef map_iterator<T2, T1> value_iterator;
friend class map_iterator<T2, T1>;
bidirectional_map() :key_root(NULL), value_root(NULL), size_(0) {}
// 复制构造函数
bidirectional_map(bidirectional_map& old) : size_(0) {
copy_tree(old);
}
// 重载赋值操作符
bidirectional_map& operator=(bidirectional_map& old) {
// 如果要赋值的这两个map不一样
// 则先删除原map中的数据再把新数据覆盖进去
if (&old != this) {
size_ = 0;
broke(key_root);
broke(value_root);
copy_tree(old);
}
return *this;
}
// 复制函数
void copy_tree(bidirectional_map &old) {
key_root = NULL;
value_root = NULL;
for (key_iterator ki = old.key_begin(); ki != old.key_end(); ki++)
insert((*ki));
}
// 容量函数
int size() { return size_; }
// 键树起点
key_iterator key_begin() { return key_iterator(key_root).begin(); }
key_iterator key_end() { return key_iterator(key_root).end(); }
// 值树起点
value_iterator value_begin() { return value_iterator(value_root).begin(); }
value_iterator value_end() { return value_iterator(value_root).end(); }
// 删除两子树并把容量清空
~bidirectional_map() {
broke(key_root);
broke(value_root);
size_ = 0;
}
// 根据键和值的大小,插入两棵排序树中
// 返回值为插入元素的迭代器和是否成功插入
pair<key_iterator, bool> insert(pair<T1, T2> const& key) {
Node<T1, T2>*kt = key_root;
Node<T2, T1>*vt = value_root;
if(key_root)
while (1) {
if (key.first < kt->val.first) {
if (kt->left)
kt = kt->left;
else
break;
}
else if (key.first > kt->val.first) {
if (kt->right)
kt = kt->right;
else
break;
}
else {
return make_pair(key_iterator(kt), false);
}
}
Node<T1, T2>*key_ = new Node<T1, T2>(key);
Node<T2, T1>*val_ = new Node<T2, T1>(make_pair(key.second, key.first));
key_->link = val_;
val_->link = key_;
if (key_root == NULL && value_root == NULL) {
key_root = key_;
value_root = val_;
}
else {
while (1) {
if (key.first < kt->val.first) {
if (kt->left)
kt = kt->left;
else {
key_->parent = kt;
kt->left = key_;
break;
}
}
else if (key.first > kt->val.first) {
if (kt->right)
kt = kt->right;
else {
key_->parent = kt;
kt->right = key_;
break;
}
}
}
while (1) {
if (key.second < vt->val.first) {
if (vt->left)
vt = vt->left;
else {
val_->parent = vt;
vt->left = val_;
break;
}
}
else if (key.second > vt->val.first) {
if (vt->right)
vt = vt->right;
else {
val_->parent = vt;
vt->right = val_;
break;
}
}
}
}
size_++;
return make_pair(key_iterator(key_), true);
}
// 根据深度打印出两棵树的形状
void print(ostream& ostr) {
ostr << "=================================================" << endl;
ostr << "KEYS:" << endl;
out(ostr, key_root, 0);
ostr << "-------------------------------------------------" << endl;
ostr << "VALUES:" << endl;
out(ostr, value_root, 0);
ostr << "=================================================" << endl;
}
// 用中序遍历递归的方法遍历并打印树
void out(ostream& ostr, Node<T1, T2>* k, int deep) {
if (!k)
return;
out(ostr, k->right, deep + 1);
for (int i = 0; i < deep; i++) ostr << " ";
ostr << k->val.first << " [" << k->val.second << "]" << endl;
out(ostr, k->left, deep + 1);
}
void out(ostream& ostr, Node<T2, T1>* k, int deep) {
if (!k)
return;
out(ostr, k->right, deep + 1);
for (int i = 0; i < deep; i++) ostr << " ";
ostr << k->val.first << " [" << k->val.second << "]" << endl;
out(ostr, k->left, deep + 1);
}
// 用后序遍历递归进行遍历然后删除树
void broke(Node<T1, T2>* key) {
if (!key)
return;
broke(key->left);
broke(key->right);
delete key;
key = NULL;
}
void broke(Node<T2, T1>* val) {
if (!val)
return;
broke(val->left);
broke(val->right);
delete val;
val = NULL;
}
// 重载【】号,用二分法快速找到需要的目标
T2 operator[](T1 key) {
Node<T1, T2>*p = key_root;
while (p) {
if (key == p->val.first)
return p->val.second;
else if (key > p->val.first)
p = p->right;
else
p = p->left;
}
return T2();
}
T1 operator[](T2 val) {
Node<T2, T1>*p = value_root;
while (p) {
if (val == p->val.first)
return p->val.second;
else if (val > p->val.first)
p = p->right;
else
p = p->left;
}
return T1();
}
// 用二分法查找map中元素
key_iterator find(T1 key) {
Node<T1, T2> *p = key_root;
while (p) {
if (key == p->val.first)
return key_iterator(p);
else if (key > p->val.first)
p = p->right;
else
p = p->left;
}
return key_end();
}
value_iterator find(T2 val) {
Node<T2, T1> *p = value_root;
while (p) {
if (val == p->val.first)
return value_iterator(p);
else if (val > p->val.first)
p = p->right;
else
p = p->left;
}
return value_end();
}
// 先根据键从键树中找出要删除的元素的身份
int erase(T1 key) {
Node<T1, T2>*p = key_root;
while (p) {
if (key == p->val.first) {
Node<T2, T1>*q = p->link;
q->link = NULL;
p->link = NULL;
erase(p->val.second, q);
erase(key, p);
size_--;
return 1;
}
else if (key > p->val.first)
p = p->right;
else
p = p->left;
}
return 0;
}
// 若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树
// 由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点
// 若*p结点只有左子树PL或右子树PR
// 此时只要令PL或PR直接成为其双亲结点*f的左子树(当*p是左子树)或右子树(当*p是右子树)即可
// 作此修改也不破坏二叉排序树的特性。
// 若*p结点的左子树和右子树均不空
// 在删去*p之后,为保持其它元素之间的相对位置不变
// 令*p的左子树为*f的左/右(依*p是*f的左子树还是右子树而定)子树
// *s为*p左子树的最右下的结点,而*p的右子树为*s的右子树
void erase(T1 k, Node<T1, T2>*&key) {
if (!key)
return;
if (k > key->val.first)
erase(k, key->right);
else if (k < key->val.first)
erase(k, key->left);
else {
Node<T1, T2> *p;
if (key->left && key->right) {
p = key->left;
while (p->right)
p = p->right;
key->val = p->val;
erase(p->val.first, p);
}
else if (key->left) {
p = key->left;
if (!key->parent)
key_root = p;
else if (key == key->parent->left)
key->parent->left = p;
else
key->parent->right = p;
// delete key;
key = NULL;
}
else if (key->right) {
p = key->right;
if (!key->parent)
key_root = p;
else if (key == key->parent->left)
key->parent->left = p;
else
key->parent->right = p;
// delete key;
key = NULL;
}
else {
p = key;
if (p->parent) {
if (p == p->parent->left) {
p->parent->left = NULL;
// delete p;
p = NULL;
}
else {
p->parent->right = NULL;
// delete p;
p = NULL;
}
}
else {
key = NULL;
// delete p;
p = NULL;
}
}
}
}
void erase(T2 k, Node<T2, T1> *&val) {
if (!val)
return;
if (k > val->val.first)
erase(k, val->right);
else if (k < val->val.first)
erase(k, val->left);
else {
Node<T2, T1> *p;
if (val->left && val->right) {
p = val->left;
while (p->right)
p = p->right;
val->val = p->val;
erase(p->val.first, p);
}
else if (val->left) {
p = val->left;
if (!val->parent)
value_root = p;
else if (val == val->parent->left)
val->parent->left = p;
else
val->parent->right = p;
// delete val;
val = NULL;
}
else if (val->right) {
p = val->right;
if (!val->parent)
value_root = p;
else if (val == val->parent->left)
val->parent->left = p;
else
val->parent->right = p;
// delete val;
val = NULL;
}
else {
p = val;
if (p->parent) {
if (p == p->parent->left) {
p->parent->left = NULL;
// delete p;
p = NULL;
}
else {
p->parent->right = NULL;
// delete p;
p = NULL;
}
}
else {
val = NULL;
// delete p;
p = NULL;
}
}
}
}
};
main.cpp (用于测试)
// ===================================================================
//
// IMPORTANT: Do not make any changes to this file, except to
// uncomment the test cases in BasicTests and
// ExtraCreditTests as you work, and to write your own test
// cases in StudentTests.
//
// ===================================================================
#include <iostream>
#include <cassert>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
#include <cstdlib>
#include "bidirectional_map.h"
// prototypes of helper functions
void BasicTests();
void StudentTests();
void ExtraCreditTests();
int main() {
//BasicTests();
StudentTests();
ExtraCreditTests();
}
void BasicTests() {
std::cout << "Beginning BasicTests()..." << std::endl;
// some simple data to put in the map
std::vector<std::string> keys;
std::vector<int> values;
keys.push_back("carrot");
keys.push_back("banana");
keys.push_back("date");
keys.push_back("fig");
keys.push_back("eggplant");
keys.push_back("apple");
values.push_back(4);
values.push_back(5);
values.push_back(6);
values.push_back(2);
values.push_back(1);
values.push_back(3);
// sorted versions of the input data for testing the iterators
std::vector<std::string> sorted_keys = keys;
std::sort(sorted_keys.begin(),sorted_keys.end());
std::vector<int> sorted_values = values;
std::sort(sorted_values.begin(),sorted_values.end());
// ------------------------------------------------
// CREATE A BIDIRECTIONAL MAP
bidirectional_map<std::string,int> bdmap;
assert (bdmap.size() == 0);
// INSERT 6 SIMPLE ONE-TO-ONE ASSOCIATIONS
// (a later test case will verify the return value of insert)
for (int i = 0; i < 6; i++) {
bdmap.insert(std::make_pair(keys[i],values[i]));
}
assert (bdmap.size() == 6);
// PRINT THE STRUCTURE
// note the formatting of this output is not strictly specified,
// but the data should be clearly presented
std::cout << "created bidirectional map with 6 associations" << std::endl;
bdmap.print(std::cout);
// ------------------------------------------------
// TEST THE SUBSCRIPT [] OPERATOR
// verify that the associations are bidirectional
for (int i = 0; i < 6; i++) {
assert (bdmap[keys[i]] == values[i]);
assert (bdmap[values[i]] == keys[i]);
}
std::cout << "bidirectional links verified" << std::endl;
// ------------------------------------------------
// TEST FIND ON KEYS
bidirectional_map<std::string,int>::key_iterator ki;
for (int i = 0; i < 6; i++) {
ki = bdmap.find(keys[i]);
assert (ki != bdmap.key_end());
assert ((*ki).first == keys[i]);
assert ((*ki).second == values[i]);
}
ki = bdmap.find("grape");
assert (ki == bdmap.key_end());
// TEST FIND ON VALUES
bidirectional_map<std::string,int>::value_iterator vi;
for (int i = 0; i < 6; i++) {
vi = bdmap.find(values[i]);
assert (vi != bdmap.value_end());
assert ((*vi).first == values[i]);
assert ((*vi).second == keys[i]);
}
vi = bdmap.find(7);
assert (vi == bdmap.value_end());
std::cout << "find on keys and find on values successful" << std::endl;
// ------------------------------------------------
// FORWARD ITERATOR ON KEYS
int counter = 0;
for (bidirectional_map<std::string,int>::key_iterator ki = bdmap.key_begin();
ki != bdmap.key_end(); ki++) {
assert (sorted_keys[counter] == (*ki).first);
std::cout << "key:" << (*ki).first << " associated with value:" << (*ki).second << std::endl;
counter++;
}
// FORWARD ITERATOR ON VALUES
counter = 0;
for (bidirectional_map<std::string,int>::value_iterator vi = bdmap.value_begin();
vi != bdmap.value_end(); vi++) {
assert (sorted_values[counter] == (*vi).first);
std::cout << "value:" << (*vi).first << " associated with key:" << (*vi).second << std::endl;
counter++;
}
// BACKWARD ITERATOR ON KEYS
counter = 5;
ki = bdmap.find(sorted_keys[counter]);
while (1) {
assert ((*ki).first == sorted_keys[counter]);
if (counter == 0) break;
counter--;
ki--;
}
// BACKWARD ITERATOR ON VALUES
counter = 5;
vi = bdmap.find(sorted_values[counter]);
while (1) {
assert ((*vi).first == sorted_values[counter]);
if (counter == 0) break;
counter--;
vi--;
}
std::cout << "forward and backward iterators on keys and values successful" << std::endl;
// ------------------------------------------------
// TESTING THE RETURN VALUE OF INSERT
// like the STL map, insert returns a pair with an iterator pointing
// to the key in the map, and a bool indicating whether the element
// was newly added, or was already there
std::pair<bidirectional_map<std::string,int>::key_iterator,bool> insert_return;
insert_return = bdmap.insert(std::make_pair("zebra",26));
assert (insert_return.first == bdmap.find("zebra"));
assert (insert_return.second == true);
insert_return = bdmap.insert(std::make_pair("apple",3));
assert (insert_return.first == bdmap.find("apple"));
assert (insert_return.second == false);
assert (bdmap.size() == 7);
bdmap.print(std::cout);
std::cout << "return value of insert correct" << std::endl;
// ------------------------------------------------
// ERASE KEY-VALUE ASSOCIATIONS FROM THE STRUCTURE
// erase returns an integer, indicating whether the specified key
// exists in the structure and was successfully erased
assert (bdmap.size() == 7);
int num_erased = bdmap.erase("zebra");
assert (num_erased == 1);
assert (bdmap.size() == 6);
bdmap.print(std::cout);
std::cout << "erased key:'zebra' and value:26" << std::endl;
num_erased = bdmap.erase("carrot");
assert (num_erased == 1);
assert (bdmap.size() == 5);
bdmap.print(std::cout);
std::cout << "erased key:'carrot' and value:4" << std::endl;
num_erased = bdmap.erase("date");
assert (num_erased == 1);
assert (bdmap.size() == 4);
bdmap.print(std::cout);
std::cout << "erased key:'date' and value:6" << std::endl;
num_erased = bdmap.erase("grape");
assert (num_erased == 0);
std::cout << "erase tests valid" << std::endl;
// ------------------------------------------------
// MORE TESTING OF THE RETURN VALUE OF INSERT
insert_return = bdmap.insert(std::make_pair("carrot",6));
assert (insert_return.first == bdmap.find("carrot"));
assert (insert_return.second == true);
insert_return = bdmap.insert(std::make_pair("apple",3));
assert (insert_return.first == bdmap.find("apple"));
assert (insert_return.second == false);
assert (bdmap.size() == 5);
bdmap.print(std::cout);
std::cout << "return value of insert correct" << std::endl;
// ------------------------------------------------
// TESTING THE FOLLOW_LINK FUNCTION
// the follow link function can be used to "jump" from an iterator
// on keys tree, to an iterator in the values tree (and vice versa)
ki = bdmap.find("fig");
assert (ki != bdmap.key_end());
assert ((*ki).first == "fig");
assert ((*ki).second == 2);
vi = ki.follow_link();
assert (vi != bdmap.value_end());
assert ((*vi).first == 2);
assert ((*vi).second == "fig");
bidirectional_map<std::string,int>::key_iterator ki2 = vi.follow_link();
assert (ki == ki2);
std::cout << "follow link function successful" << std::endl;
std::cout << "BasicTests() completed" << std::endl;
}
void StudentTests() {
std::cout << "Beginning StudentTests()..." << std::endl;
//
// Add your own test cases here
//
// Be sure to test:
// * copy constructor
// * assignment operator
// * bidirectional_maps with other data types
// * all corner cases of insert, find, and erase
//
int num[] = { 8, 4, 12, 2, 1, 3, 6, 5, 7, 10, 9, 11, 14, 13, 15 };
char fu[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' };
bidirectional_map<char, int> mmap, nmap;
for (int i = 0; i < 15; i++)
mmap.insert(std::make_pair(fu[i], num[i]));
mmap.print(std::cout);
nmap = mmap;
nmap.print(std::cout);
bidirectional_map<char, int> omap(mmap);
bidirectional_map<char, int>::key_iterator n_it(nmap.key_begin()), m_it(mmap.key_begin());
for (; n_it != nmap.key_end() && m_it != mmap.key_end(); n_it++, m_it++) {
assert((*n_it) == (*m_it));
}
omap.erase('c');
omap.erase('g');
omap.erase('m');
omap.erase('l');
omap.print(std::cout);
assert((*omap.find('e')).second == num[4]);
std::cout << "key:" << (*omap.find('e')).first << " associated with value:" << (*omap.find('e')).second << std::endl;
omap.insert(std::make_pair('z', 26));
omap.print(std::cout);
std::cout << "StudentTests() completed" << std::endl;
}
void ExtraCreditTests() {
/*
// uncomment if you implement non one-to-one associations
std::cout << "Beginning ExtraCreditTests()..." << std::endl;
// these tests illustrate how non one-on-one associations are
// stored, queried, and removed from the structure
// some simple data to put in the map
std::vector<std::string> keys;
keys.push_back("cat");
keys.push_back("dog");
keys.push_back("cat");
keys.push_back("bird");
keys.push_back("cat");
keys.push_back("dog");
keys.push_back("bird");
std::vector<int> values;
values.push_back(4);
values.push_back(6);
values.push_back(1);
values.push_back(6);
values.push_back(2);
values.push_back(1);
values.push_back(1);
// sorted versions of the input data for testing the iterators
std::vector<std::string> sorted_keys = keys;
std::sort(sorted_keys.begin(),sorted_keys.end());
std::vector<int> sorted_values = values;
std::sort(sorted_values.begin(),sorted_values.end());
// ------------------------------------------------
// INSERT NON ONE-TO-ONE ASSOCIATIONS
// insert a number of associations, note that in this example we
// have duplicates in both keys and values
bidirectional_map<std::string,int> bdmap;
for (int i = 0; i < 6; i++) {
bdmap.insert(std::make_pair(keys[i],values[i]));
}
assert (bdmap.size() == 6);
bdmap.print(std::cout);
// FAILED INSERT
// however, when we try to insert an association that is already in
// the structure the insert returns false
std::pair<bidirectional_map<std::string,int>::key_iterator,bool> insert_result;
insert_result = bdmap.insert(std::make_pair("cat", 2));
assert ((*insert_result.first).first == "cat");
assert ((*insert_result.first).second == 2);
assert (insert_result.second == false);
assert (bdmap.size() == 6);
// ANOTHER SUCCESSFUL INSERT
insert_result = bdmap.insert(std::make_pair(keys[6],values[6]));
assert (insert_result.first == bdmap.find("bird"));
assert (insert_result.second == true);
assert (bdmap.size() == 7);
bdmap.print(std::cout);
// ------------------------------------------------
// FORWARD ITERATOR ON KEYS
std::cout << "forward iteration over keys:" << std::endl;
unsigned int counter = 0;
for (bidirectional_map<std::string,int>::key_iterator ki = bdmap.key_begin();
ki != bdmap.key_end(); ki++) {
std::cout << " key:" << (*ki).first << " associated with value:" << (*ki).second << std::endl;
assert ((*ki).first == sorted_keys[counter]);
counter++;
}
assert (counter == bdmap.size());
// FORWARD ITERATOR ON VALUES
std::cout << "forward iteration over values:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::value_iterator vi = bdmap.value_begin();
vi != bdmap.value_end(); vi++) {
std::cout << " value:" << (*vi).first << " associated with key:" << (*vi).second << std::endl;
assert ((*vi).first == sorted_values[counter]);
counter++;
}
assert (counter == bdmap.size());
bdmap.print(std::cout);
// ------------------------------------------------
// ERASE AN ASSOCIATION
// erase a single association between key:cat and value:4
int num_erased = bdmap.erase("cat", 4);
bdmap.print(std::cout);
assert (num_erased == 1);
assert (bdmap.size() == 6);
std::cout << "erased key:cat and value:4" << std::endl;
sorted_keys.erase(sorted_keys.begin()+4);
sorted_values.erase(sorted_values.begin()+4);
// verify iteration over keys and values
std::cout << "forward iteration over keys:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::key_iterator ki = bdmap.key_begin();
ki != bdmap.key_end(); ki++) {
std::cout << " key:" << (*ki).first << " associated with value:" << (*ki).second << std::endl;
assert ((*ki).first == sorted_keys[counter]);
counter++;
}
assert (counter == bdmap.size());
std::cout << "forward iteration over values:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::value_iterator vi = bdmap.value_begin();
vi != bdmap.value_end(); vi++) {
std::cout << " value:" << (*vi).first << " associated with key:" << (*vi).second << std::endl;
assert ((*vi).first == sorted_values[counter]);
counter++;
}
assert (counter == bdmap.size());
bdmap.print(std::cout);
// ------------------------------------------------
// FAILED ERASE
// try to erase an association that does not exist
num_erased = bdmap.erase("bird", 2);
assert (num_erased == 0);
assert (bdmap.size() == 6);
// ------------------------------------------------
// ERASE ALL WITH SPECIFIC KEY
// erase all associations with "dog" as the key
num_erased = bdmap.erase("dog");
assert (num_erased == 2);
assert (bdmap.size() == 4);
bdmap.print(std::cout);
std::cout << "erased all key:dog associations" << std::endl;
sorted_keys.erase(sorted_keys.begin()+5);
sorted_keys.erase(sorted_keys.begin()+4);
sorted_values.erase(sorted_values.begin()+5);
sorted_values.erase(sorted_values.begin()+2);
// verify iteration over keys and values
std::cout << "forward iteration over keys:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::key_iterator ki = bdmap.key_begin();
ki != bdmap.key_end(); ki++) {
std::cout << " key:" << (*ki).first << " associated with value:" << (*ki).second << std::endl;
assert ((*ki).first == sorted_keys[counter]);
counter++;
}
assert (counter == bdmap.size());
std::cout << "forward iteration over values:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::value_iterator vi = bdmap.value_begin();
vi != bdmap.value_end(); vi++) {
std::cout << " value:" << (*vi).first << " associated with key:" << (*vi).second << std::endl;
assert ((*vi).first == sorted_values[counter]);
counter++;
}
assert (counter == bdmap.size());
// ------------------------------------------------
// ERASE ALL WITH SPECIFIC VALUE
// erase all associations with 1 as the value
num_erased = bdmap.erase(1);
assert (num_erased == 2);
assert (bdmap.size() == 2);
bdmap.print(std::cout);
std::cout << "erased all value:1 associations" << std::endl;
bdmap.print(std::cout);
sorted_keys.erase(sorted_keys.begin()+2);
sorted_keys.erase(sorted_keys.begin());
sorted_values.erase(sorted_values.begin()+1);
sorted_values.erase(sorted_values.begin());
// verify iteration over keys and values
std::cout << "forward iteration over keys:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::key_iterator ki = bdmap.key_begin();
ki != bdmap.key_end(); ki++) {
std::cout << " key:" << (*ki).first << " associated with value:" << (*ki).second << std::endl;
assert ((*ki).first == sorted_keys[counter]);
counter++;
}
assert (counter == bdmap.size());
std::cout << "forward iteration over values:" << std::endl;
counter = 0;
for (bidirectional_map<std::string,int>::value_iterator vi = bdmap.value_begin();
vi != bdmap.value_end(); vi++) {
std::cout << " value:" << (*vi).first << " associated with key:" << (*vi).second << std::endl;
assert ((*vi).first == sorted_values[counter]);
counter++;
}
assert (counter == bdmap.size());
*/
std::cout << "ExtraCreditTests() completed" << std::endl;
}
正确的测试输出
Beginning BasicTests()...
created bidirectional map with 6 associations
=================================================
KEYS:
fig [2]
eggplant [1]
date [6]
carrot [4]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
6 [date]
5 [banana]
4 [carrot]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
bidirectional links verified
find on keys and find on values successful
key:apple associated with value:3
key:banana associated with value:5
key:carrot associated with value:4
key:date associated with value:6
key:eggplant associated with value:1
key:fig associated with value:2
value:1 associated with key:eggplant
value:2 associated with key:fig
value:3 associated with key:apple
value:4 associated with key:carrot
value:5 associated with key:banana
value:6 associated with key:date
forward and backward iterators on keys and values successful
=================================================
KEYS:
zebra [26]
fig [2]
eggplant [1]
date [6]
carrot [4]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
26 [zebra]
6 [date]
5 [banana]
4 [carrot]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
return value of insert correct
=================================================
KEYS:
fig [2]
eggplant [1]
date [6]
carrot [4]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
6 [date]
5 [banana]
4 [carrot]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
erased key:'zebra' and value:26
=================================================
KEYS:
fig [2]
eggplant [1]
date [6]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
6 [date]
5 [banana]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
erased key:'carrot' and value:4
=================================================
KEYS:
fig [2]
eggplant [1]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
5 [banana]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
erased key:'date' and value:6
erase tests valid
=================================================
KEYS:
fig [2]
eggplant [1]
carrot [6]
banana [5]
apple [3]
-------------------------------------------------
VALUES:
6 [carrot]
5 [banana]
3 [apple]
2 [fig]
1 [eggplant]
=================================================
return value of insert correct
follow link function successful
BasicTests() completed