C++(数据结构与算法):32---散列(哈希)冲突之链地址法(附编码实现)

一、链地址法

  • 散列表基础介绍与散列冲突见文章:https://blog.csdn.net/qq_41453285/article/details/103517420
  • 链地址法的基本思想:在散列表的每一个位置分配一个线性表。这样,每一个数对就可以存储在它的起始桶线性表中了
  • 链地址法同时也解决了散列表移除的问题
  • 例如,下图就是一个散列表,其中产生散列冲突的元素存储在同一个桶链表中了

二、链地址法的增加、删除、查找

查找

  • 根据关键字k与散列函数f(k)=k%D,计算起始桶。然后搜索该桶对应的链表

增加:

  • 首先保证散列表没有一个记录与该记录的关键字相同,为此而进行的搜索仅限于该记录的起始桶所对应的链表

删除:

  • 删除一个关键字为k的记录,先搜索它所对应的起始桶链表,找到该记录然后删除

三、一种改进的实现方法

  • 在每个链表上增加一个尾节点,可以改进一些程序的性能
  • 尾节点的关键字值最起码要比插入的所有数对的关键字都大
  • 有了尾节点就省去了在链表中出现的大部分对空指针的检验操作
  • 备注:每个链表都有不同的尾节点,实际上,所有的链表可共用一个尾节点
  • 例如在下图中,尾节点的关键字用表示(实际中用整数表示),可以使用limits.h头文件中定义的INT_MAX来替代

 

四、与线性探查法的比较

五、与跳表的比较

六、编码实现1

  • 我们在前面用链表实现过字典(sortedChain),因为链地址法中散列表的每一个位置都是一个链表,因此我们可以直接定义一个类,在类中使用前面文章介绍过的sortedChain来实现链地址法
  • sortedChain实现见文章:https://blog.csdn.net/qq_41453285/article/details/103449056

头文件定义 

#include <iostream>
#include <string>
#include <utility>

using std::cout;
using std::cin;
using std::endl;
using std::pair;
using std::string;

hash类定义

  • 用于将关键字映射为一个整数,便于在散列中进行操作
template<class K> class hash;

template<>
class hash<std::string>
{
public:
	std::size_t operator()(const std::string theKey)const
	{
		unsigned long hashValue = 0;
		int length = (int)theKey.length();
		for (int i = 0; i < length; i++) {
			hashValue = hashValue * 5 + theKey.at(i);
		}

		return std::size_t(hashValue);
	}
};

template<>
class hash<int>
{
public:
	std::size_t operator()(const int theKey) const
	{
		return std::size_t(theKey);
	}
};

template<>
class hash<long>
{
public:
	std::size_t operator()(const long theKey) const
	{
		return std::size_t(theKey);
	}
};

字典链表定义

  • 链地址法中,散列表的每一个位置都是一个链表,链表就使用下面的sortedChain表示
template<class K,class E>
struct pairNode
{
	typedef std::pair<const K, E> pairType;

	pairType element;
	struct pairNode<K,E>* next;

	pairNode(const pairType& thePair):element(thePair) {}
	pairNode(const pairType& thePair, struct pairNode<K, E>* theNext) :element(thePair) { this->next = theNext; }
};


template<class K, class E>
class sortedChain :public dictionary<K, E>
{
public:
	sortedChain();
	~sortedChain();
	bool empty()const override;
	int size()const override;
	std::pair<const K, E>* find(const K& theKey)const override;
	void erase(const K& theKey) override;
	void insert(const std::pair<const K, E>& thePair) override;

	void output(std::ostream &out)const;
private:
	pairNode<K, E>* firstNode;
	int dSize;
};

template<class K, class E>
sortedChain<K, E>::sortedChain()
{
	this->firstNode = nullptr;
	this->dSize = 0;
}

template<class K, class E>
sortedChain<K, E>::~sortedChain()
{
	pairNode<K, E>* tempNode;
	while (this->firstNode) {
		tempNode = this->firstNode->next;
		delete this->firstNode;
		this->firstNode = tempNode;
	}
}

template<class K, class E>
bool sortedChain<K, E>::empty()const
{
	return this->dSize == 0;
}

template<class K, class E>
int sortedChain<K, E>::size()const
{
	return this->dSize;
}

template<class K, class E>
std::pair<const K, E>* sortedChain<K, E>::find(const K& theKey)const
{
	pairNode<K, E>* tempNode = this->firstNode;

	//遍历字典
	while ((tempNode != nullptr) && (tempNode->element.first != theKey))
		tempNode = tempNode->next;

	//如果找到了就返回
	if ((tempNode != nullptr) && (tempNode->element.first == theKey))
		return &tempNode->element;

	//没有找到就返回空
	return nullptr;
}

template<class K, class E>
void sortedChain<K, E>::erase(const K& theKey)
{
	pairNode<K, E>* eraseNode = this->firstNode;
	pairNode<K, E>* beforeEraseNode = nullptr;

	//遍历字典
	while ((eraseNode != nullptr) && (eraseNode->element.first < theKey))
	{
		beforeEraseNode = eraseNode;
		eraseNode = eraseNode->next;
	}

	//找到了就删除
	if ((eraseNode != nullptr) && (eraseNode->element.first == theKey))
	{
		//如果删除的是头结点
		if (eraseNode == this->firstNode) {
			this->firstNode = eraseNode->next;
		}
		else {
			beforeEraseNode->next = eraseNode->next;
		}
		delete eraseNode;
		this->dSize--;
	}
}

template<class K, class E>
void sortedChain<K, E>::insert(const std::pair<const K, E>& thePair)
{
	pairNode<K, E>* afterInsertNode = this->firstNode;
	pairNode<K, E>* beforeInsertNode = nullptr;

	//遍历字典
	while ((afterInsertNode != nullptr) && (afterInsertNode->element.first < thePair.first))
	{
		beforeInsertNode = afterInsertNode;
		afterInsertNode = afterInsertNode->next;
	}

	//如果键已经存在,更新值
	if ((afterInsertNode != nullptr) && (afterInsertNode->element.first == thePair.first))
	{
		afterInsertNode->element.second = thePair.second;
		return;
	}

	//如果键不存在,插入新节点
	pairNode<K, E>* insertNode = new pairNode<K, E>(thePair, afterInsertNode);
	if (beforeInsertNode == nullptr)
		this->firstNode = insertNode;
	else
		beforeInsertNode->next = insertNode;
	this->dSize++;
}

template<class K, class E>
void sortedChain<K, E>::output(std::ostream &out)const
{
	for (pairNode<K, E>* tempNode = this->firstNode; tempNode != nullptr; tempNode = tempNode->next) {
		out << "key:" << tempNode->element.first << ",value:" << tempNode->element.second << std::endl;
	}
}

template<class K,class E>
std::ostream& operator<<(std::ostream& out,const sortedChain<K,E>& theChain)
{
	theChain.output(out);
	return out;
}

散列表的最终实现 

template<class K,class E>
class dictionary
{
public:
	virtual ~dictionary(){}
	virtual bool empty()const = 0;
	virtual int size()const = 0;
	virtual std::pair<const K, E>* find(const K& theKey)const = 0;
	virtual void erase(const K& theKey) = 0;
	virtual void insert(const std::pair<const K, E>& thePair) = 0;
};

template<class K,class E>
class hashChains :public dictionary<K, E>
{
public:
	hashChains(int theDivisor=11);
	~hashChains();
	bool empty()const override;
	int size()const override;
	std::pair<const K, E>* find(const K& theKey)const override;
	void erase(const K& theKey) override;
	void insert(const std::pair<const K, E>& thePair) override;

	void output(std::ostream & out)const;
private:
	sortedChain<K, E>* table;
	hash<K> hash;  //将关键字映射为一个整数
	int dSize;     //哈希表的大小
	int divisor;   //哈希函数的除数
};

template<class K, class E>
hashChains<K, E>::hashChains(int theDivisor = 11)
{
	this->divisor = theDivisor;
	this->dSize = 0;

	this->table = new sortedChain<K, E>[theDivisor];
}

template<class K, class E>
hashChains<K, E>::~hashChains()
{
	if (this->table) {
		delete[] this->table;
		this->table = nullptr;
	}
}

template<class K, class E>
bool hashChains<K, E>::empty()const
{
	return (this->dSize == 0);
}

template<class K, class E>
int hashChains<K, E>::size()const
{
	return this->dSize;
}

template<class K, class E>
std::pair<const K, E>* hashChains<K, E>::find(const K& theKey)const
{
	int index = hash(theKey) % this->divisor;
	return this->table[index].find(theKey);
}

template<class K, class E>
void hashChains<K, E>::erase(const K& theKey)
{
	int index = hash(theKey) % this->divisor;
	this->table[index].erase(theKey);
}

template<class K, class E>
void hashChains<K, E>::insert(const std::pair<const K, E>& thePair)
{
	int bucketIndex = hash(thePair.first) % this->divisor;
	int homeSize = this->table[bucketIndex].size();
	this->table[bucketIndex].insert(thePair);

	//如果insert操作不是替换值,而是插入新值,那么就增加元素个数
	if (this->table[bucketIndex].size() > this->dSize) {
		this->dSize++;
	}
}

template<class K, class E>
void hashChains<K, E>::output(std::ostream & out)const
{
	for (int i = 0; i < this->divisor; ++i) {
		if (this->table[i].empty())
			std::cout << "NULL" << std::endl;
		else
			std::cout << this->table[i];
	}
}

template<class K,class E>
std::ostream& operator<<(std::ostream &out,const hashChains<K,E>& theHashChains)
{
	theHashChains.output(out);
	return out;
}

主函数

int main()
{
	hashChains<int, int>* myHashChains = new hashChains<int, int>(11);
	
	myHashChains->insert(std::pair<int, int>(1, 1));
	myHashChains->insert(std::pair<int, int>(2, 2));
	myHashChains->insert(std::pair<int, int>(3, 3));

	std::cout << myHashChains;

	return 0;
}

七、编码实现2

  • 待续
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值