我才学完AVL,感觉AVL好像能实现类似map这样的功能,于是做了这样一个模板类。(后来知道stl的map也是用平衡树实现的,但是是用内建的红黑树,更高级一点)
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
/*
Map使用起来就像一个强大的数组,但是你不需要事先声明它的范围,它的下标可以任意地大(不溢出);甚至不局限于整数,你甚至可以
用float、double、结构体、类(注1)作为它的下标,然后来访问它。
另外,即使你的下标很大,但是它所使用的空间只与你使用过的有关!
例如:
a1[2032974248889]=90;
a2[2389.5353]=34;
都是可以实现的。
Map实质上是个映射集。从它的下标(下面我们称为关键字),映射到对应的值。
声明一个Map,它可以存储若干映射,你可以通过关键字来访问这些映射。
使用:
1.定义
Map<关键字类型,映射值类型> 变量名;
用这样的形式来定义Map;
例如:
Map<int, char> a;
2.添加映射(赋值)
下面两个方式都是可以的。
a[299342]='S';
a.MappingAt(299342)='S';
3.单个访问
printf("%c",a[299342]);
如果这个关键字之前并未出现,但你使用了它,那么自动创建这个映射,它的值为默认值。
4.连续访问
连续访问不建议用a[i]这样的形式来访问。因为定位一个关键字需要时间(O(logN),N为a中所有映射的个数),尽管它不算耗时。
但是使用迭代器,进行连续访问,时间复杂度是O(N)(由于数据结构,事实上每个节点会在迭代过程中访问两遍,但是编程者无需关心。)
a的迭代器it是这样声明的:
a.Iterator it;
for(it=a.Begin();it!=a.End;it=it->Next())
{
printf("%d ",it->Value);
}
Begin()返回a中第一个映射指针,End是最后一个映射的下一个无效的指针。Next()返回下一个映射指针。
他们是按照关键字的大小顺序排列的。
逆序的话,是这样的:
for(it=a.End->Last;it!=End;it=it->Last())
{
printf("%d ",it->Value);
}
!无论是逆序或正序迭代,最后都是在End结束。
*请留意Begin()是方法函数,但End是指针变量。
5.删除映射
对于迭代器it
使用Delete(it);
或者关键字x
使用Delete(x);
如果这个关键字没有使用过,即没有对应的映射值,将不会做任何操作。
这个Map是用查找树来存储映射的。查找树使用AVL来维护平衡。
注1:
如果关键字类型是结构体或类的话,你需要定义大小比较和判断相等的重载操作符(> < ==)。
或者建议写一个函数,将这个特殊类型转换成数值,将这个数值作为关键字类型,便于比较。例如字符串就可以这样。
*/
template <class KeyType,class ValueType>
class Map
{
public:
struct Treenode
{
Treenode *Left=nullptr;
Treenode *Right=nullptr;
Treenode *Father=nullptr;
KeyType Key;
ValueType Value;
int Height=1;
int GetHeight()
{
int lh=Left==nullptr?0:Left->Height;
int rh=Right==nullptr?0:Right->Height;
Height=lh>rh?lh+1:rh+1;
return Height;
}
void AddToLeft(Treenode *T)
{
Left=T;
T->Father=this;
}
void AddToRight(Treenode *T)
{
Right=T;
T->Father=this;
}
void RemoveFromTree()
{
if(this==Father->Left)Father->Left=nullptr;
else Father->Right=nullptr;
}
Treenode* Next()
{
Treenode* p;
if(Right==nullptr)
{
p=Father;
while(p->Key<Key&&p->Father!=nullptr)
p=p-&