大师兄的数据结构学习笔记(十四): 词典

2020-12-21  本文已影响0人  superkmi

大师兄的数据结构学习笔记(十三): KD树
大师兄的数据结构学习笔记(十五): 串

一、关于符号表

二、实现词典

接口 功能
get(key) 若关键码key存在,则返回对应的数据项value;否则返回NULL。
put(key,value) 插入词条(key,value)。
remove(key) 若关键码key存在,则删除对应词条;否则返回NULL。
template <typename K, typename V>
//字典条目
struct Entry {
    K key; V value;
    Entry(K k = K(), V v = V()) :key(k), value(v) {}
    Entry(const Entry<K, V>& e) :key(e.key), value(e.value) {}
    bool operator<(const Entry<K, V>& e) { return key < e.key; }
    bool operator>(const Entry<K, V>& e) { return key > e.key; }
    bool operator==(const Entry<K, V>& e) { return key == e.key; }
    bool operator!=(const Entry<K, V>& e) { return key != e.key; }
};

template<typename K, typename V>
//字典结构
struct 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<class T>
// 四联表节点
struct QualdListNode
{
    T entry; //词条
    QualdListNode<T>* pred; //前驱
    QualdListNode<T>* succ; //后继
    QualdListNode<T>* above; //上邻
    QualdListNode<T>* below; //下邻

    QualdListNode(
        T e = T(), QualdListNode<T>* p = nullptr,
        QualdListNode<T>* s = nullptr,
        QualdListNode<T>* a = nullptr,
        QualdListNode<T>* b = nullptr) :
        entry(e), pred(p), succ(s), above(a), below(b) {};

    QualdListNode<T>* insertAsSuccAbove(T const& e, QualdListNode<T>* b = nullptr); // 插入新结点,以当前节点为前驱,以节点b为下邻
};

template<class T>
// 插入新结点,以当前节点为前驱,以节点b为下邻
QualdListNode<T>* QualdListNode<T>::insertAsSuccAbove(T const& e, QualdListNode<T>* b)
{
    QualdListNode<T>* node;
    node->T = e;
    node->below = b;
    node->above = b->above;
    b->above = node;
    return node;
}

template<class T>
//四联表
class QuadList
{
public:
    QuadList(); //构造函数
    ~QuadList(); //构造函数
    int size() const; //获得规模
    bool is_empty() const; //是否为空
    QualdListNode<T>* first() const; //首节点
    QualdListNode<T>* last() const; //尾节点
    T remove(QualdListNode<T>* p); //删除结点
    QualdListNode<T>* insertAfterAbove(T const& e, QualdListNode<T>* p, QualdListNode<T>* b = nullptr); //插入结点
private:
    int _size;
    bool empty();
    QualdListNode<T>* header;
    QualdListNode<T>* trailer;
    bool valid(QualdListNode<T>* p); //判断是否合法
protected:
    void init(); // 创建时初始化
    int clear(); // 清除所有节点
};

template<class T>
//构造函数
QuadList<T>::QuadList()
{
    init();
}

template<class T>
//析构函数
QuadList<T>::~QuadList()
{
    clear();
    delete header;
    delete trailer;
}

template<class T>
//获得规模
int QuadList<T>::size() const
{
    return _size;
}

template<class T>
//是否为空
bool QuadList<T>::is_empty() const
{
    return _size == 0;
}

template<class T>
//首节点
QualdListNode<T>* QuadList<T>::first()const
{
    return header;
}

template<class T>
//尾节点
QualdListNode<T>* QuadList<T>::last() const
{
    return trailer;
}

template<class T>
//判断是否合法
bool QuadList<T>::valid(QualdListNode<T>* p)
{
    return p && (p != header) && (p != trailer);
}

template<class T>
//判断是否为空
bool QuadList<T>::empty()
{
    return _size == 0;
}

template<class T>
//初始化表
void QuadList<T>::init()
{
    header = new QualdListNode<T>; //头哨兵节点
    trailer = new QualdListNode<T>; //尾哨兵节点
    header->succ = trailer; //横向哨兵
    header->pred = nullptr;
    trailer->pred = header;
    trailer->succ = nullptr;
    header->above = nullptr;
    header->below = nullptr;
    trailer->above = nullptr;
    trailer->below = nullptr;
    _size = 0;
}

template<class T>
//删除所有节点
int QuadList<T>::clear()
{
    int oldsize = _size;
    while (_size > 0)
    {
        remove(header->succ);
        return oldsize;
    }
}

template<class T>
//删除节点
T QuadList<T>::remove(QualdListNode<T>* p)
{
    p->pred->succ = p->succ;
    p->succ->pred = p->pred;
    T e = p->entry;
    delete p;
    return e;
}

template<class T>
//插入结点
QualdListNode<T>* QuadList<T>::insertAfterAbove(T const& e, QualdListNode<T>* p, QualdListNode<T>* b)
{
    _size++;
    return p->insertAsSuccAbove(e, b);
}
template<typename K, typename V>
//跳转表
class Skiplist :public Dictionary<K, V>, public List<QuadList<Entry<K, V>>* >
{
protected:
    bool skipSearch(ListNode<QuadList<Entry<K, V>>*>*& qlist, QualdListNode<Entry<K, V>>*& p, K& k); //内部查找算法
public:
        int size() const { return empty() ? 0 : last()->data->size(); };
        int level() { return List:: size(); };
        bool put(K k, V v); //插入词条
        V* get(K k);
        bool remove(K k);
};

template<typename K, typename V>
//内部查找算法
bool Skiplist<K,V>::skipSearch(ListNode<QuadList<Entry<K, V>>*>*& qlist, QualdListNode<Entry<K, V>>*& p, K& k)
{
    while (true) //遍历每一层
    {
        while (p->succ && (p->entry.key <= k)) //从前到后找更大的key
        {
            p = p->succ;
        }
        p = p->pred; //往回走一个结点
        if (p->pred && (k == p->entry.key))
        {
            return true;
        }
        qlist = qlist->succ; //转入后一层
        if (!qlist->succ) //查询失败
        {
            return false;
        }
        p = (p->pred) ? p->below : qlist->data->first(); // 转至当前塔的下一个结点
    }
}

template<typename K, typename V>
//插入词条
bool Skiplist<K,V>::put(K k, V v)
{
    Entry<K, V> e = Entry<K, V>(k, v); //待插入词条
    if (empty())
    {
        insertAsFirst(new QuadList<Entry<K, V>>); //插入首个Entry
    }
    ListNode<QuadList<Entry<K, V>>*>* qlist = first(); //四联表顶层
    QualdListNode<Entry<K, V>>* p = qlist->data->first(); //首节点
    if (skipSearch(qlist, p, k)) //如果找到结点
    {
        while (p->below)
        {
            p = p->below; //转到塔底
        }
    }
    qlist = last();
    QualdListNode<Entry<K, V>>* b = qlist->data->insertAfterAbove(e, p);
    while (rand() & 1)
    {
        while (qlist->data->valid(p) && !p->above)
        {
            p = p->pred;
        }
        if (!qlist->data->valid(p))
        {
            if (qlist == first())
            {
                insertAsFirst(new QuadList<Entry<K, V>>);
            }
            p = qlist->pred->data->first()->pred;
        }
        else
        {
            p = p->above;
        }
        qlist = qlist->pred;
        b = qlist->data->insertAfterAbove(e, p, b);
    }
    return true;
}

template<typename K, typename V>
V* Skiplist<K, V>::get(K k)
{
    if (empty())
    {
        return nullptr;
    }
    ListNode<QuadList<Entry<K, V>>*>* qlist = first(); //从顶层获得quadlist
    QualdListNode<Entry<K, V>>* p = qlist->data->first(); //首节点开始
    return skipSearch(qlist, p, k) ? &(p->entry.value) : nullptr; //返回值
}

template<typename K, typename V>
bool Skiplist<K, V>::remove(K k)
{
    if (empty()) //空表
    {
        return false;
    }
    ListNode<QuadList<Entry<K, V>>*>* qlist = first();
    QualdListNode<Entry<K, V>>* p = qlist->data->first();
    if (!skipSearch(qlist, p, k)) //如果搜索不到词条
    {
        return false;
    }
    do {
        QuadListNode<Entry<K, V>>* lower = p->below; //记住下一层结点
        qlist->data->remove(p); //删除当前层结点
        p = lower;
        qlist = qlist->succ;
    } while (qlist->succ);
    
    while (!empty() && first()->data->empty())
    {
        List::remove(first());
    }
    return true;
}

四、散列表

1. 数组、链表和散列表
2. 关于散列表
3.关于完美散列
4. 关于散列函数
方法 简介
直接定址法 即 f(key) = a*key + b,f表示散列函数,a、b是常量,key是键值。
数字分析法 即对数字做左移、右移、反转等操作获取散列值。
除数留余法 即 f(key) = key % p,p 表示容器数量,这种方式通常用在将数据存放到指定容器中,如何决定哪个数据放到哪个容器,比如分表后插入数据如何处理(此时p表示拆分后数据表的数量),分布式Redis如何存放数据(此时p表示几台Redis服务器)。
随机数法 即 f(key) = random(key),比如负载均衡的random机制。
5. 关于散列冲突
5.1 开放寻址法

1) 线性探测法(Linear Probing)

  • 插入数据:当往散列表中插入数据时,如果某个数据经过散列函数之后,存储的位置已经被占用了,就从当前位置开始,依次往后查找(到底后从头开始),看是否有空闲位置,直到找到为止。
  • 查找数据:通过散列函数求出要查找元素的键值对应的散列值,然后比较数组中下标为散列值的元素和要查找的元素是否相等,若相等,则说明就是我们要查找的元素;否则,就顺序往后依次查找。如果遍历到数组的空闲位置还未找到,就说明要查找的元素并没有在散列表中。
  • 删除数据:为了不让查找算法失效,可以将删除的元素特殊标记为deleted,当线性探测查找的时候,遇到标记为deleted的空间,并不是停下来,而是继续往下探测。

2) 二次探测(Quadratic probing)

  • 线性探测每次探测的步长为1,即在数组中一个一个探测,而二次探测的步长变为原来的平方。

3) 双重散列(Double hashing)

  • 使用一组散列函数,直到找到空闲位置为止。
5.2 链表法
方法 优点 缺点 适用场景
开放寻址法 1、数据存储在数组,可以有效利用CPU缓存加速查询速度
2、序列化简单
1、删除需要特殊标记已删除数据
2、所有数据存储在一个数组,发生冲突时,解决的代价更高,造成装载因子不能太大,使得更加浪费内存空间
1、数据量小
2、装载因子小
链表法 1、内存利用率高,需要时再申请
2、对大装载因子容忍度高,可大于1
1、因为链表需要存储指针,存储指针需要消耗内存,不适合小对象存储
2、链表节点不是连续空间,因此CPU缓存不友好
1、存储大对象、大数据量的散列表
2、支持更多优化策略,如红黑树代替链表。
5.3 实现散列表
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;

template<class T>
//散列表节点
struct Node
{
    T key;
    string name;
    Node<T>* next;
};

template<class T>
//散列表
class hashTable
{
public:
    hashTable(); //构造函数
    void insert(T key, string name); //插入
    void del(T key);
    void del(T key,string name); //删除
    Node<T>* search(T key);
    Node<T>* search(T key,string name);
private:
    void insert(Node<T>* node);
    void del(Node<T>* node); 
private:
    vector<Node<T>*> _map;
    int _hash(T key);  //哈希函数
};

template<class T>
int hashTable<T>::_hash(T key)
{
    return (key * 50) % 41; //hashfunciton算法
}

template<class T>
hashTable<T>::hashTable()
{
    _map.resize(100);
    for (int i = 0; i <= 50; i++)
    {
        _map[i] = nullptr;
    }
}

template<class T>
//插入
void hashTable<T>::insert(Node<T>* node)
{
    node->next = _map[_hash(node->key)];
    _map[_hash(node->key)] = node;
}

template<class T>
void hashTable<T>::insert(T key, string name)
{
    Node<T>* node = new Node<T>;
    node->key = key;
    node->name = name;
    insert(node);
}

template<class T>
//删除
void hashTable<T>::del(Node<T>* node)
{
    del(node->key, node->name);
}

template<class T>
void hashTable<T>::del(T key)
{
    if (_map[_hash(key)] == nullptr)return; //如果没有对应key
    int num = _hash(key);
    Node<T>* node = _map[num];
    while (_map[num])
    {
        node = _map[num];
        _map[num] = node->next;
        delete node;
    }
}

template<class T>
void hashTable<T>::del(T key, string name)
{
    if (_map[_hash(key)] == nullptr)return;
    int num = _hash(key);
    Node<T>* p = _map[num];
    Node<T>* q = _map[num];
    while (p)
    {
        if (p->name == name)break;
        q = p;
        p = p->next;
    }
    if (q == _map[num] && p == q)
    {
        _map[num] = p->next;
    }
    else
    {
        q->next = p->next;
    }
}

template<class T>
//查找
Node<T>* hashTable<T>::search(T key)
{
    return _map[_hash(key)];

}

template<class T>
//查找
Node<T>* hashTable<T>::search(T key,string name)
{
    if (!_map(_hash(key)))return nullptr;
    Node<T>* node = _map[_hash(key)];
    while (node)
    {
        if (node->name == name)
        {
            return node;
        }
        node = node->next;
    }
    return nullptr;
}
上一篇 下一篇

猜你喜欢

热点阅读