散列表

不定期更新

/*
*散列表:我们日常存储数据通常通过数组来达到目的,但是对于我们需要存储key-value
*属性时,如果元素是日益增长的,我们开辟大空间或者动态申请数组(需要复制旧数组),
*不免是种资源浪费,这时散列表就能起到很好的作用,通过散列函数将不同元素尽可能映射
*到不同槽,从而得到一种尽可能的达到数组(查找,插入,删除,更新)的高效(用下标为key),
*而又不产生对资源的极大浪费。
1.链接法:我们要设计一个散列表,就需要一个槽的结构(能通过散列函数hash(key)找到的位置)(把槽放入散列表中,元素放入槽中)
,链接法就是采用n : m的形式,把 n个元素映射到m个槽中,使得每个槽都有 a = n / m个元素,
a称为装载因子,然后槽内结构就采用链表或双链表实现
2.开放寻址法:这种散列表是把元素全部放入散列表中,此时的每个元素构成了槽序列,我们需要通过一种计算方式(探查序列)来找出未被占用的槽
存放元素,或者找到对应的槽观察是否是我们想要操作的元素
*/

我们先来实现简单的散列表,通过链接法实现的,并可接受用户指定的hash

我们先来链接法槽的概念:

#pragma once
#include "nocopyable.h"
template<typename K,typename V>
class Slot :nocopyable{
private:
    class Element {
    public:
        Element(const K& k, const V& v);
        const K& getKey() const { return key; }
        V& getValue() { return value; }
        const V& getValue() const { return value; }
        void setValue(const V& v) { value = v; }
        Element *next;
        Element *last;
    private:
        Element(const Element&) = delete;
        Element& operator =(const Element&) = delete;
        K key;
        V value;
    };
public:
    Slot() :head(nullptr) {}
    typename Slot::Element* add(const K& k, const V& v);
    V find(const K& k);
    V& operator[](const K& k);
    const V& operator[](const K& k)const;
    void update(const K& k,const V& v);
    void remove(const K& k);
    ~Slot();

    Element *head;
private:
    Element *getElement(const K& k);
    void deleteElement(const K& k);
};
template<typename K,typename V>
Slot<K,V>::Element::Element(const K& k,const V& v):key(k),value(v),next(nullptr),last(nullptr) {

}

template<typename K,typename V>
typename Slot<K,V>::Element* Slot<K,V>::add(const K& k,const V& v) {
    if (head == nullptr) {
        head = new Slot::Element(k,v);
        return head;
    }else {
        Slot::Element *h = head;
        while (h->next != nullptr) {
            h = h->next;
        }
        h->next = new Slot::Element(k, v);
        h->next->last = h;
        return h->next;
    }
}
template<typename K,typename V>
typename Slot<K,V>::Element* Slot<K,V>::getElement(const K& k) {
    Element *h = head;
    while (h != nullptr) {
        if (h->getKey() == k) {
            return h;
        }
        h = h->next;
    }
    return nullptr;
}
template<typename K,typename V>
void Slot<K,V>::deleteElement(const K& k) {
    Slot::Element *e = getElement(k);
    if (e == nullptr) {
        return;
    }
    Slot::Element *last = e->last;
    Slot::Element *next = e->next;
    last->next = next;
    next->last = last;
    delete e;
}
template<typename K,typename V>
 V Slot<K,V>::find(const K& k) {
     Slot::Element *e = getElement(k);
     if (e == nullptr) {
         throw "can't the value with the key";
     }
     return e->getValue();
}
 template<typename K,typename V>
 void Slot<K,V>::update(const K& k,const V& v) {
     Slot::Element *e = getElement(k);
     if (e == nullptr) {
         add(k, v);
     }else {
         e->setValue(v);
     }
 }
 template<typename K,typename V>
 void Slot<K,V>::remove(const K& k) {
     deleteElement(k);
 }
 template<typename K,typename V>
 V& Slot<K,V>::operator[](const K& k) {
     Slot::Element *e = Slot::getElement(k);
     if (e == nullptr) {
         e = add(k, V{});
         return e->getValue();
     }else {
         return e->getValue();
     }
 }
 template<typename K,typename V>
 const V& Slot<K,V>:: operator[](const K& k) const{
     const Slot::Element *e = getElement(k);
     if (e == nullptr) {
         throw "can't try to change const value";
     }else {
         return e->getValue();
     }
 }
 template<typename K,typename V>
 Slot<K,V>::~Slot() {
     if (head == nullptr) {
         return;
     }
     while (head->next != nullptr) {
         Slot::Element *e = head;
         head = head->next;
         delete e;
     }
 }

提供链接法哈希函数接口:

#pragma once
#include <functional>
#include <cmath>
using std::function;
using std::placeholders::_1;
using std::placeholders::_2;
using std::bind;
template<typename K>
class Hash {
public:
    Hash(const int& s);
    Hash(const int& s,const function<int(const K&)>& h);
    void setDefaultHash();
    void setMultiHash();
    void setMultiHash(const double& r);
    void setUserHash(const function<int(const K&)>& h);
    int operator()(const K& k);
private:
    function<int(const K&)> hash;
    int slotCount;
    //除法散列法
    int defaultHash(const K& k) {
        return k % slotCount;
    }
    //乘法散列法
    int multiHash(const K& k,const double& r) { 
        return static_cast<int>(slotCount * fmod(k * r, 1));
    }
};
template<typename K>
Hash<K>::Hash(const int& s):slotCount(s){
    hash = bind(&Hash::defaultHash,this,_1);
}
template<typename K>
Hash<K>::Hash(const int& s,const function<int(const K&)>& h):hash(h),slotCount(s) {

}
template<typename K>
void Hash<K>::setDefaultHash() {
    hash = bind(&Hash::defaultHash, *this, _1);
}
template<typename K>
void Hash<K>::setMultiHash() {
    hash = bind(&Hash::multiHash, this, _1, 0.618);
}
template<typename K>
void Hash<K>::setMultiHash(const double& r) {
    hash = bind(&Hash::multiHash, this, _1, r);
}
template<typename K>
void Hash<K>::setUserHash(const function<int(const K& k)>& h) {
    hash = h;
}
template<typename K>
int Hash<K>::operator()(const K& k) {
    return hash(k);
}
#pragma once
class nocopyable {
public:
    nocopyable() = default;
    ~nocopyable() = default;
private:
    nocopyable(const nocopyable&) = delete;
    nocopyable& operator=(const nocopyable&) = delete;
};

小测试:

#include <iostream>
#include "MyMap.h"
#include <functional>

using namespace std;
/*
*散列表:我们日常存储数据通常通过数组来达到目的,但是对于我们需要存储key-value
*属性时,如果元素是日益增长的,我们开辟大空间或者动态申请数组(需要复制旧数组),
*不免是种资源浪费,这时散列表就能起到很好的作用,通过散列函数将不同元素尽可能映射
*到不同槽,从而得到一种尽可能的达到数组(查找,插入,删除,更新)的高效(用下标为key),
*而又不产生对资源的极大浪费。
1.链接法:我们要设计一个散列表,就需要一个槽的结构(能通过散列函数hash(key)找到的位置)(把槽放入散列表中,元素放入槽中)
,链接法就是采用n : m的形式,把 n个元素映射到m个槽中,使得每个槽都有 a = n / m个元素,
a称为装载因子,然后槽内结构就采用链表或双链表实现
2.开放寻址法:这种散列表是把元素全部放入散列表中,此时的每个元素构成了槽序列,我们需要通过一种计算方式(探查序列)来找出未被占用的槽
存放元素,或者找到对应的槽观察是否是我们想要操作的元素
*/
int main() {
    MyMap<int, int> m;
    m[5] = 4;
    m[5] = 3;
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值