目录
1. 头文件
1.1 词典头文件
template <typename K, typename V> struct Dictionary { //词典Dictionary模板类
virtual int size() const = 0; //当前词条总数
virtual bool put(K, V) = 0; //插入词条(禁止雷同词条时可能失败)
virtual V* get(K k) = 0; //读取词条
virtual bool remove(K k) = 0; //删除词条
};
1.2 词条头文件
//词条模板类
template <typename K, typename V> struct Entry {
K key; V value; //关键码、数值
Entry(K k = K(), V v = V()) : key(k), value(v) {}; //默认构造函数
Entry(Entry<K, V> const& e) : key(e.key), value(e.value) {}; //基于克隆的构造函数
bool operator< (Entry<K, V> const& e) { return key < e.key; } //比较器:小于
bool operator> (Entry<K, V> const& e) { return key > e.key; } //比较器:大于
bool operator== (Entry<K, V> const& e) { return key == e.key; } //判等器:等于
bool operator!= (Entry<K, V> const& e) { return key != e.key; } //判等器:不等于
}; //得益于比较器和判等器,从此往后,不必严格区分词条及其对应的关键码
1.3 列表头文件
请点击
C++ 数据结构学习 ---- 列表_孤城寻欢的博客-CSDN博客_c++列表
1.4 四链表头文件
#include "QuadListNode.h" //引入Quadlist节点类
template <typename T> struct Quadlist { //四联列表
int _size; //规模
QNodePosi<T> header, trailer; //头哨兵、尾哨兵
void init(); //初始化
int clear(); //清除所有节点
Quadlist() { init(); } //构造
~Quadlist() { clear(); delete header; delete trailer; } //析构
T remove(QNodePosi<T> p); //删除(合法)位置p处的节点,返回被删除节点的数值
QNodePosi<T> insert(T const& e, QNodePosi<T> p, QNodePosi<T> b = NULL); //将e作为p的后继、b的上邻插入
void traverse(void (*) (T&)); //遍历各节点,依次实施指定操作(函数指针,只读或局部修改)
template <typename VST> void traverse(VST&); //遍历
}; //Quadlist
//Quadlist初始化,创建Quadlist对象时统一调用
template <typename T> void Quadlist<T>::init() {
header = new QNode<T>; //创建头哨兵节点
trailer = new QNode<T>; //创建尾哨兵节点
header->succ = trailer; header->pred = NULL; //沿横向联接哨兵
trailer->pred = header; trailer->succ = NULL; //沿横向联接哨兵
header->above = trailer->above = NULL; //纵向的后继置空
header->below = trailer->below = NULL; //纵向的前驱置空
_size = 0; //记录规模
} //如此构造的四联表,不含任何实质的节点,且暂时与其它四联表相互独立
//将e作为p的后继、b的上邻插入Quadlist
template <typename T> QNodePosi<T>
Quadlist<T>::insert(T const& e, QNodePosi<T> p, QNodePosi<T> b)
{
_size++; return p->insert(e, b);
} //返回新节点位置(below = NULL)
//删除Quadlist内位置p处的节点,返回其中存放的词条
template <typename T>
T Quadlist<T>::remove(QNodePosi<T> p) { //assert: p为Quadlist中的合法位置
p->pred->succ = p->succ; p->succ->pred = p->pred; _size--;//摘除节点
T e = p->entry; delete p; //备份词条,释放节点
return e; //返回词条
}
//清空Quadlist
template <typename T> int Quadlist<T>::clear() {
int oldSize = _size;
while (0 < _size) remove(header->succ); //逐个删除所有节点
return oldSize;
}
//遍历Quadlist,对各节点依次实施visit操作
template <typename T> void Quadlist<T>::traverse(void (*visit) (T&)) { //利用函数指针机制,只读或局部性修改
QNodePosi<T> p = header;
while ((p = p->succ) != trailer) visit(p->data);
}
//遍历Quadlist,对各节点依次实施visit操作
template <typename T> template <typename VST> void Quadlist<T>::traverse(VST& visit) { //利用函数对象机制,可全局性修改
QNodePosi<T> p = header;
while ((p = p->succ) != trailer) visit(p->data);
}
//将e作为当前节点的后继、b的上邻插入Quadlist
template <typename T> QNodePosi<T> QNode<T>::insert(T const& e, QNodePosi<T> b) {
QNodePosi<T> x = new QNode<T>(e, this, succ, NULL, b); //创建新节点
succ->pred = x; succ = x; //设置水平逆向链接
if (b) b->above = x; //设置垂直逆向链接
return x; //返回新节点的位置
}
1.5 四链表节点头文件
#include "Entry.h"
template <typename T> struct QNode;
template <typename T> using QNodePosi = QNode<T>*; //跳转表节点位置
template <typename T> struct QNode { //四联节点
T entry; //所存词条
QNodePosi<T> pred, succ, above, below; //前驱、后继、上邻、下邻
QNode(T e = T(), QNodePosi<T> p = NULL, QNodePosi<T> s = NULL,
QNodePosi<T> a = NULL, QNodePosi<T> b = NULL) //构造器
: entry(e), pred(p), succ(s), above(a), below(b) {}
QNodePosi<T> insert(T const& e, QNodePosi<T> b = NULL); //将e作为当前节点的后继、b的上邻插入
};
1.6 跳转表头文件
#include "Dictionary.h"//引入词典
#include "Entry.h"//引入词条
#include "List.h"//引入列表
#include "Quadlist .h"//引入四联表
template <typename K, typename V> //key、value
//符合Dictionary接口的Skiplist模板类(隐含假设元素之间可比较大小)
struct Skiplist : public Dictionary<K, V>, public List< Quadlist< Entry<K, V> >* > {
Skiplist() { this->insertAsFirst(new Quadlist< Entry<K, V> >); }; //即便为空,也有一层空列表
QNodePosi< Entry<K, V> > search(K); //由关键码查询词条
int size() const { return this->empty() ? 0 : this->last()->data->_size; } //词条总数
int height() { return this->size(); } //层高 == #Quadlist
V* get(K); //读取
bool put(K, V); //插入(Skiplist允许词条重复,故必然成功)
bool remove(K); //删除
};
2. 相关函数
2.1 查找函数
//跳转表词条查找算法
template <typename K, typename V> V* Skiplist<K, V>::get(K k) {
QNode< Entry<K, V> >* p = search(k); //无论是否命中,search()都不会返回NULL
return (p->pred && p->entry.key == k) ? &(p->entry.value) : NULL; //故须再做判断
} //有多个命中时靠后者优先
2.2 删除函数
//跳转表词条删除算法
template <typename K, typename V> bool Skiplist<K, V>::remove(K k) {
QNodePosi< Entry<K, V> > p = search(k); //查找目标词条
if (!p->pred || (k != p->entry.key)) return false; //若不存在,直接返回
ListNodePosi< Quadlist< Entry<K, V> >* > qlist = this->last(); //从底层Quadlist开始
while (p->above) { qlist = qlist->pred; p = p->above; } //升至塔顶
do { //逐层拆塔
QNodePosi< Entry<K, V> > lower = p->below; //记住下一层节点,并
qlist->data->remove(p); //删除当前层节点,再
p = lower; qlist = qlist->succ; //转入下一层
} while (qlist->succ); //直到塔基
while ((1 < height()) && (this->first()->data->_size < 1)) { //逐层清除
//List<K>::remove(this->first());
this->first()->data->header->above = NULL;
} //已不含词条的Quadlist(至少保留最底层空表)
return true; //删除成功
} //体会:得益于哨兵的设置,哪些环节被简化了?
/******************************************************************************************
* Skiplist词条查找算法(供内部调用)
* 返回关键码不大于k的最后一个词条(所对应塔的基座)
******************************************************************************************/
template <typename K, typename V> QNodePosi< Entry<K, V> > Skiplist<K, V>::search(K k) {
for (QNodePosi< Entry<K, V> > p = this->first()->data->header; ; ) //从顶层Quadlist的首节点p出发
if ((p->succ->succ) && (p->succ->entry.key <= k)) { p = p->succ; } //尽可能右移
else if (p->below) { p = p->below; } //水平越界时,下移
else return p; //验证:此时的p符合输出约定(可能是最底层列表的header)
} //体会:得益于哨兵的设置,哪些环节被简化了?
2.3 插入函数
//跳转表词条插入算法
template <typename K, typename V> bool Skiplist<K, V>::put(K k, V v) {
Entry<K, V> e = Entry<K, V>(k, v); //待插入的词条(将被同一塔中所有节点共用)
QNodePosi< Entry<K, V> > p = search(k); //查找插入位置:新塔将紧邻其右,逐层生长
ListNodePosi< Quadlist< Entry<K, V> >* > qlist = this->last(); //首先在最底层
QNodePosi< Entry<K, V> > b = qlist->data->insert(e, p); //创建新塔的基座
while (rand() & 1) { //经投掷硬币,若确定新塔需要再长高,则
while (p->pred && !p->above) p = p->pred; //找出不低于此高度的最近前驱
if (!p->pred && !p->above) { //若该前驱是header,且已是最顶层,则
this->insertAsFirst(new Quadlist< Entry<K, V> >); //需要创建新的一层
this->first()->data->header->below = qlist->data->header;
qlist->data->header->above = this->first()->data->header;
}
p = p->above; qlist = qlist->pred; //上升一层,并在该层
b = qlist->data->insert(e, p, b); //将新节点插入p之后、b之上
} //课后:调整随机参数,观察总体层高的相应变化
return true; //Dictionary允许重复元素,插入必成功
} //体会:得益于哨兵的设置,哪些环节被简化了?
3. 完整代码
#include<iostream>
#include <ctime>
#include "Dice.h"
using namespace std;
// 测试跳转表
template <typename K, typename V> void testSkiplist(int n) {
Skiplist<K, V> L;
while (L.size() < n)
switch (dice(3)) {
case 0: { //查找,成功率 <= 33.3%
K key = dice((K)n * 3); //[0, 3n)范围内的key
cout << "搜索:" << key << "..." << endl;
V* pValue = L.get(key);
if(pValue)
cout << "找到了:" << (*pValue) << endl;
else
cout << "没有找到!" << endl;
break;
}
case 1: { //删除,成功率 <= 33.3%
K key = dice((K)n * 3); //[0, 3n)范围内的key
cout << "删除:" << key << "...";
// printf("Removing "); print(key); printf(" ... ");
if (L.remove(key)) {
cout << "删除成功!" << endl; //printf("Done\n"); print(L);
}
else
cout << "词条不存在!" << endl;//printf("Entry not exists\n\n");
break;
}
default: {//插入,成功率 == 100%
K k = dice((K)n * 3); V v = (V)'A' + dice(26); //在[0, 2n)*['A'~'Z']范围内的词条
cout << "插入:<" << k << "," << v << ">...";
//printf("Inserting <"); print(k); printf(","); print(v); printf("> ... ");
L.put(k, v);
cout << "插入成功!" <<endl;//printf("Done\n");print(L);
break;
}
}
while (L.size() > 0) {
K key = dice((K)n * 3); //[0, 3n)范围内的key
cout << "删除:" << key << "...";// printf("Removing "); print(key); printf(" ... ");
if (L.remove(key))
cout << "删除完成!" << endl;
else
cout << "词条不存在!" << endl;
// L.remove(key) ? printf("Done\n") : printf("Entry not exists\n");
// print(L); printf("\n");
}
}
int main() {
testSkiplist<int, char>(5); //元素类型可以在这里任意选择
system("pause");
return 0;
}
4. 运行结果及截图
以上所有代码参考邓俊辉老师的《c++语言版数据结构》!