参考书籍:数据结构、算法与应用:C++语言描述(原书第2版) (美)萨尼
算法导论(原书第3版)
1.问题定义及需求分析
在现实中我们经常需要使用字典,那么字典是怎么实现的呢?C++是中的map(字典)是基于红黑树实现的,它在查找插入删除方法具有很好的时间性能logn,最初想实现红黑树,由于一些分析和理解不到位没能修复bug,最终转变为实现了简单的搜索树及通过迭代器遍历之。
实现功能包括:insert,前置iterator遍历,find操作等。
2.概要设计
主要运用模板,内嵌类,链表,重载等方面的知识实现。
3.详细设计
首先是字典里的元素pair类,通过struct及模板实现
template<class K, class E>
struct h_pair
{
public:
K first;
E second;
h_pair(){}
h_pair(K a,E b):first(a),second(b){}
~h_pair(){}
};
接着是一个模板类map,它需要一个头指针及一些函数的封装
template<class K, class E>
class h_map
{
node<K, E> * root, *bg;//bg保存第一个最小指针的位置
void fixup(node<K, E>*z);
void efixup(node<K, E>*x);
void left_rotate(node<K, E> *x);
void right_rotate(node<K, E> *y);
void transplant(node<K, E> *u, node<K, E> *v);//替代函数
int num;
public:
h_map() { bg = root = NULL; num = 0; }
int size()const { return num; }
bool empty() const { return num == 0; }
void insert(h_pair <K, E> &thepair);
node<K, E>* find( K key) const;
void erase(K thekey);
node<K, E> * begin() { return bg; }
node<K, E> * end() { return NULL; }
class iterator;//内嵌类做map的迭代器
~h_map(){}
};
分析里面的函数,其中最重要的是insert,erasere及find。
Insert函数:
红黑树的思想是按照在每次插入节点后通过左旋或者右旋的方式修复这棵树,由于测试中fixup函数存在bug,所以最终的insert函数没有调用fixup,这棵树也就退化成了一颗搜索树,插入的思想是首先找到插入的位置,因为搜索树中是左子树的值小于中心节点而中心节点小于右子树。故通过比较和替换的方式实现inset操作。
template<class K, class E>
void h_map<K, E>::insert(h_pair < K,E> &thepair)
{
node<K, E> *newnode = new node<K, E>;
newnode->element.first = thepair.first;
newnode->element.second = thepair.second;
newnode->left = newnode->right = NULL;
newnode->color = 1;//上色//先开辟一个新的节点空间,并赋值
if (root == NULL)//还没有节点的情况下
{
root = newnode;
root->p = NULL;//没有父亲
root->color = 0;//上黑色
bg = root;
++num;
return;//提前退出
}
node<K,E> *p = root,*pp=NULL;//pp代表当前节点的父亲
while (p != NULL)
{
pp = p;
if (thepair.first < p->element.first)
{
p = p->left;
}
else if (thepair.first > p->element.first)
{
p = p->right;
}
else //如果key相等,直接覆盖提前退出
{
p->element.second = thepair.second;
return;
}
}
++num;
newnode->p = pp;//p为当前节点插入的位置,所以他的父亲是pp
if (thepair.first > pp->element.first)
{
pp->right = newnode;
}
else
pp->left = newnode;
//fixup(newnode);
p = root;//p用来遍历bg是否需要矫正
while (p->left!= NULL)
p = p->left;
bg = p;//矫正
}
erase函数
由于insert函数的bug未能解决,故eraser未能使用。
find函数
因为搜索树的元素满足左子树小于根小于右子树的性质,实现较为简单
template<class K, class E>
node< K, E>* h_map<K, E>::find(K key) const
{
node < K, E> *p= root;
while (p != NULL)
{
if (p->element.first < key)
p = p->right;
else if (p->element.first > key)
p = p->left;
else
return p;//找到了返回其中的element
}
return NULL;//未找到
}
迭代器:通过内嵌类实现限制迭代器的定义方式,另一方面通过指针的传递,重载前置++,--、->,*,==,!=等实现
template<class K, class E>
class h_map<K,E>:: iterator
{
node< K, E> *it;
public:
iterator() { it = NULL; }
iterator(node< K, E> *p)
{
it = p;
}
h_pair<K, E> * operator ->()
{
return &(it->element);
}
h_pair<K, E> operator *()
{
return (it->element);
}//重载->和*
void operator =(node<K, E>* p)
{
it = p;
}//重载=
bool operator ==(node<K, E>* p)
{
return it == p;
}//重载==
bool operator !=(node<K, E>* p)
{
return it != p;
}//重载!=
重点是前置++和—是和顺序表不一样的,顺序表里,前置++,--非常的简单,因为线性存储,你只需要改下位置,但搜索树不同,如前置++,首先你要判断它有没有右孩子,有就找里面最左的(最小的),如果有左孩子就找左孩子里右边找最大的。所以前置++和--,相对顺序表来说效率更低。
void operator ++()
{
if (it == NULL)
{
throw ;
}
if (it->right != NULL)//当当前节点有右孩子的情况
{
it = it->right;
while(it->left!=NULL)
it = it->left;
}
else
{
while ((it->p != NULL)&&(it == it->p->right))
it = it->p;
it = it->p;
}
}//重载前置++
void operator --()
{
if (it == NULL)
{
throw;
}
if (it->left != NULL)//当当前节点有左孩子的情况
{
it = it->left;
while (it->right != NULL)
it = it->right;
}
else
{
while ((it->p != NULL) && (it == it->p->left))
it = it->p;
it = it->p;
}
}//重载前置--
~iterator() {};
};//内嵌类做map的迭代器*/
4.调试分析
通过VS调试,解决了一部分问题。
搜索树的算法复杂度为o(logn),但由于缺少修正操作等最坏情况下将会退化成o(n)。
感想:希望加深对红黑树的理解,等寒假或者以后有时间一定要把bug找出来并改正。
5.使用说明and6.测试结果:
7.附录
#include "stdafx.h"
#include<iostream>
#include<string>
using namespace std;
template<class K, class E>
struct h_pair
{
public:
K first;
E second;
h_pair(){}
h_pair(K a,E b):first(a),second(b){}
~h_pair(){}
};
template<class K,class E>
struct node
{
h_pair< K, E> element;//pair.first代表key,pair.second代表value
bool color;//0代表黑色,1代表红色
node *p;
node *left;
node *right;
};
template<class K, class E>
class h_map
{
node<K, E> * root, *bg;//bg保存第一个最小指针的位置
void fixup(node<K, E>*z);
void efixup(node<K, E>*x);
void left_rotate(node<K, E> *x);
void right_rotate(node<K, E> *y);
void transplant(node<K, E> *u, node<K, E> *v);//替代函数
int num;
public:
h_map() { bg = root = NULL; num = 0; }
int size()const { return num; }
bool empty() const { return num == 0; }
void insert(h_pair <K, E> &thepair);
node<K, E>* find( K key) const;
void erase(K thekey);
node<K, E> * begin() { return bg; }
node<K, E> * end() { return NULL; }
class iterator;//内嵌类做map的迭代器
~h_map(){}
};
template<class K, class E>
void h_map<K,E>:: efixup(node<K, E>* x)
{
node<K, E> *w;
while(x!=root&&x->color==0)
{
if (x == x->p->left)
{
w = x->p->right;//找兄弟
if (w->color == 1)
{
w->color = 0;
x->p->color = 1;
left_rotate(x->p);
w = x->p->right;//重新找w
}
if (w->left->color == 0 && w->right->color == 0)
{
w->color = 1;
x = x->p;
}
else
{
if (w->right->color == 0)
{
w->left->color = 0;
w->color = 1;
right_rotate(w);
w = x->p->right;
}
w->color = x->p->color;
x->p->color = 0;
w->right->color = 0;
left_rotate(x->p);
break;
}
}
else
{
w = x->p->left;//找兄弟
if (w->color == 1)
{
w->color = 0;
x->p->color = 1;
right_rotate(x->p);
w = x->p->left;//重新找w
}
if (w->left->color == 0 && w->right->color == 0)
{
w->color = 1;
x = x->p;
}
else
{
if (w->left->color == 0)
{
w->right->color = 0;
w->color = 1;
left_rotate(w);
w = x->p->left;
}
w->color = x->p->color;
x->p->color = 0;
w->right->color = 0;
right_rotate(x->p);
break;
}
}
}
}
template<class K, class E>
class h_map<K,E>:: iterator
{
node< K, E> *it;
public:
iterator() { it = NULL; }
iterator(node< K, E> *p)
{
it = p;
}
h_pair<K, E> * operator ->()
{
return &(it->element);
}
h_pair<K, E> operator *()
{
return (it->element);
}//重载->和*
void operator =(node<K, E>* p)
{
it = p;
}//重载=
bool operator ==(node<K, E>* p)
{
return it == p;
}//重载==
bool operator !=(node<K, E>* p)
{
return it != p;
}//重载!=
void operator ++()
{
if (it == NULL)
{
throw ;
}
if (it->right != NULL)//当当前节点有右孩子的情况
{
it = it->right;
while(it->left!=NULL)
it = it->left;
}
else
{
while ((it->p != NULL)&&(it == it->p->right))
it = it->p;
it = it->p;
}
}//重载前置++
void operator --()
{
if (it == NULL)
{
throw;
}
if (it->left != NULL)//当当前节点有左孩子的情况
{
it = it->left;
while (it->right != NULL)
it = it->right;
}
else
{
while ((it->p != NULL) && (it == it->p->left))
it = it->p;
it = it->p;
}
}//重载前置--
~iterator() {};
};//内嵌类做map的迭代器*/
template<class K, class E>
node< K, E>* h_map<K, E>::find(K key) const
{
node < K, E> *p= root;
while (p != NULL)
{
if (p->element.first < key)
p = p->right;
else if (p->element.first > key)
p = p->left;
else
return p;//找到了返回其中的element
}
return NULL;//未找到
}
template<class K, class E>
void h_map<K, E>::insert(h_pair < K,E> &thepair)
{
node<K, E> *newnode = new node<K, E>;
newnode->element.first = thepair.first;
newnode->element.second = thepair.second;
newnode->left = newnode->right = NULL;
newnode->color = 1;//上色//先开辟一个新的节点空间,并赋值
if (root == NULL)//还没有节点的情况下
{
root = newnode;
root->p = NULL;//没有父亲
root->color = 0;//上黑色
bg = root;
++num;
return;//提前退出
}
node<K,E> *p = root,*pp=NULL;//pp代表当前节点的父亲
while (p != NULL)
{
pp = p;
if (thepair.first < p->element.first)
{
p = p->left;
}
else if (thepair.first > p->element.first)
{
p = p->right;
}
else //如果key相等,直接覆盖提前退出
{
p->element.second = thepair.second;
return;
}
}
++num;
newnode->p = pp;//p为当前节点插入的位置,所以他的父亲是pp
if (thepair.first > pp->element.first)
{
pp->right = newnode;
}
else
pp->left = newnode;
//fixup(newnode);
p = root;//p用来遍历bg是否需要矫正
while (p->left!= NULL)
p = p->left;
bg = p;//矫正
}
template<class K, class E>
void h_map<K, E>::left_rotate(node<K, E> *x)
{
if (x->right == NULL)
throw "左旋时右孩子不能为空";
node<K, E> *y = x->right;
x->right = y->left;
if (y->left != NULL)
y->left->p = x;//考虑y的左孩子是否为空的情况
y->p = x->p;
if (x->p == NULL)
root = y;//如果x是第一个节点的的话
else if (x == x->p->left)
y = x->p->left;
else
y = x->p->right;//使x的父亲变成y的父亲
y->left = x;
x->p = y;//使x的父亲变成y
}
template<class K, class E>
void h_map<K, E>::right_rotate(node<K, E> *y)
{
if (y->left == NULL)
throw "右旋时左孩子不能为空";
node<K, E> *x = y->left;
y->left = x->right;
if (x->right != NULL)
x->right->p = y;//考虑x的右孩子是否为空的情况
x->p = y->p;
if (y->p == NULL)
root = x;//如果y是第一个节点的的话
else if (y == y->p->left)
x = y->p->left;
else
x= y->p->right;//使y的父亲变成x的父亲
x->right = y;
y->p = x;//使y的父亲变成x
}
template<class K, class E>
void h_map<K, E>::fixup(node<K,E>*z)
{
node<K, E> *y;
while (z->p!=NULL&&z->p->color == 1)//我们处理时是局部为红色,所以如果他的父亲为红色,则说明处理未完成
{
cout << '1';
if (z->p == z->p->p->left)//父亲是左孩子的情况
{
y = z->p->p->right;//先找叔叔
if (y->color == 1)//如果叔叔也是红色的话
{
z->p->color = 0;
y->color = 0;
y->p->color = 1;
z = y->p;//祖父成为新的破坏平衡的点
}
else
{
if (z == z->p->right)
{
z = z->p;
left_rotate(z);
}//如果叔叔是黑色,且我是右孩子,先左旋让我变成左孩子
z->p->color = 0;
y->p->color = 1;
right_rotate(y->p);
}
}
else//父亲是右孩子的情况
{
y = z->p->p->left;//先找叔叔
if (y->color == 1)//如果叔叔也是红色的话
{
z->p->color = 0;
y->color = 0;
y->p->color = 1;
z = y->p;//祖父成为新的破坏平衡的点
}
else
{
if (z == z->p->left)
{
z = z->p;
right_rotate(z);
}//如果叔叔是黑色,且我是左孩子,先右旋让我变成右孩子
z->p->color = 0;
y->p->color = 1;
left_rotate(y->p);
}
}
}
if (z->p == NULL)//已经上升到root的情况
z->color = 0;
}
template<class K, class E>
void h_map<K, E>::transplant(node<K, E>*u, node<K, E>*v)
{
if (u->p == NULL)
root = v;
else if (u == u->p->left)
v = u->p->left;
else if (u == u->p->right)
v = u->p->right;
u->p = v->p;
}
template<class K, class E>
void h_map<K, E>::erase(K thekey)
{
bool origin_color;
node <K, E>*p = find(thekey);//先找位置
node <K, E>*rp;//用来替代的节点
node <K, E>*x=NULL;//用来替代的节点的孩子
if (p == NULL)
throw;
origin_color = p->color;
--num;
if (p->left == NULL)//p只有右孩子的情况
{
transplant(p, p->right);
rp = p->right;
delete p;
}
else if(p->right == NULL)//p只有右孩子的情况
{
transplant(p, p->left);
rp = p->left;
delete p;
}
else//p有两个孩子的情况
{
rp = p->right;
while (rp->left != NULL)
rp = rp->left;
origin_color = rp->color;
x = rp->right;
if (rp == p->right)//用来替代的节点,直接就是rp的右孩子的情况
{
x->p = rp->p;
transplant(p, rp);
rp->left = p->left;
p->left->p = rp;
delete p;
}
else
{
x->p = rp;
transplant(p, rp);
p->left = rp->left;
p->right = rp->right;
p->right->p=p->left->p = rp;
}
}
if(rp!=NULL)
rp->color = p->color;
/*if (origin_color == 0&&x!=NULL)
{
efixup(x);
}*/
}
template<class K, class E>
void inorder(node<K, E> *p)
{
if (p != NULL)
{
inorder(p->left);
cout << p->element.second;
inorder(p->right);
}
}
int main()
{
int i;
h_map<int, string> a;//初始化图
h_map<int, string>::iterator d;//初始化迭代器
h_pair<int, string> b;//元素
b.first = 2;
b.second = "第2号";
a.insert(b);
b.first = 4;
b.second = "第4号";
a.insert(b);
b.first = 5;
b.second = "第5号";
a.insert(b);
b.first = 1;
b.second = "第1号";
a.insert(b);
b.first = 6;
b.second = "第6号";
a.insert(b);
b.first = 7;
b.second = "第7号";
a.insert(b);
b.first = 3;
b.second = "第3号";
a.insert(b);//以打乱的方式插入7个元素
for (d = a.begin(); d != a.end(); ++d)
cout << d->second<<endl;//通过迭代器遍历输出
d=a.find(6);
--d;//测试find函数和减法应该输出5号元素
if (d==NULL)
cout << "no found";
else
cout <<"键值为:" <<d->first<<"对应"<<d->second<<endl<<"字典所含元素为:"<<a.size();//输出元素和大小
return 0;
}