不定期更新
/*
*散列表:我们日常存储数据通常通过数组来达到目的,但是对于我们需要存储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;
}