数据结构与算法分析-C++描述 第5章 散列ADT(分离链接法)

散列的基本思想:

      理想的散列表数据结构只不过是一些包含一些的具有固定大小的数组。一般由数据成员构成,表的大小currentSize为散列数据的一部分,通常表的大小从0到(currentSize - 1),这为散列函数带来了处理上面的方便。

散列函数

       将每个键映射到0到(currentSize - 1)范围内的某个数,并将其放到适当的存储单元中。理想情况下散列函数应该计算简单而且保证任何两个不同的键映射到不同的单元,实际上这是不可能实现的,因为键值无限而存储单元有界,因此需要寻找一个散列函数能够“尽可能均匀”的分布键,同时,散列函数还具体解决当两个键映射到同一单元时(称为冲突)问题及调整表的大小的功能。

      一个较好的散列函数:H_k = \sum_{i = 0}^{keysize - 1}(key_{[i \mod keysize-1]} \times 37^{i}),即计算37的多项式函数。该散列函数允许溢出,可能产生负数,通过修正该溢出,使用取模运算保证键值仍在存储单元范围内。

冲突(collision):

       当两个键映射到同一单元时将发生存储冲突。解决冲突的两种常用方法:分离链接法(sparate chaining)开放定址法。下图为分离链接法图示(此处hash(x) = x \mod 10,通常模数为素数,将更有利于数据的删除和插入操作),即将同一值得所有元素保存到同一链表中。

 

装填因子(load factor) : \lambda = \dfrac {elements \ in \ table }{totalSize \ of \ table}.

      数学推论证明,对于分离链接法,散列表的大小并不重要,而装填因子才是最重要的,对于分离散列表而言,应满足:\lambda \approx 1,当装填因子超过1时,使用再散列方法重新分配存储单元的大小。

再散列:

       重新分配存储单元大小,通常为大于原始表currentSize  2倍的最小素数。

散列优点:

      以常数平均时间执行插入、删除、查找的技术。

实例:创建基于分离链接法的散列表,完成插入、删除、再散列、查询、清除等功能。

1、hash.h

//hash.h
#ifndef HASH_H_
#define HASH_H_

#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<algorithm>
#include<cmath>
#include<iomanip>

using std::cout;
using std::endl;
using std::string;
using std::ostream;
using std::vector;
using std::list;
using std::setw;

class Student{
private:
	string name;
	string course;
	double score;
public:
	//Student constructor
	Student(const string &n, const string &c, double s):name(n), course(c), score(s){
	};
	//non parameter constructor
	Student():name(""), course(""), score(0.0){
	
	}
	
	//Student destructor
	~Student(){
	};
	
	//get name 
	const string & getName() const{
		return name;
	};
	
	//overload operator==
	bool operator==(const Student &stu) const{
		return name == stu.name;
	};
	
	//overload operator!=
	bool operator!=(const Student &stu) const{
		return !(*this == stu);
	};
	
	//overload operator<<
	friend ostream & operator<<(ostream &os, const Student &stu){
		cout << " name : " << stu.name << setw(8) << " course : " << setw(8) << stu.course << " score : " << stu.score << endl;
		return os;
	}	
};

//hash function
int hash(const string &key){
	int hashVal = 0;
	for(int i = 0; i < key.length(); i++){
		hashVal = 37 * hashVal + key[i];
	}
	
	return hashVal;
};

//overload hash function
int hash(const Student &stu){
	return hash(stu.getName());
};

template<class T>
class HashTable{
private:
	vector<list<T> > table;
	int currentSize;
public:
	//HashTable constructor
	HashTable(int num = 11):table(num), currentSize(0){
	};
	
	//HashTable destructor
	~HashTable(){
	};
	
	//judge whether the table contain obj return T obj
	T findElement(const T &obj){
		list<T> &temp = table[myhash(obj)];
		typename list<T>::iterator itr = find(temp.begin(), temp.end(), obj);
		if(itr != temp.end()){
			return *itr;
		}else{
			T temp1;
			return temp1;
		}
	};
	
	//judge whether table contain obj reutrn bool
	bool contain(const T &obj) const{
		const list<T> &temp = table[myhash(obj)];
		return find(temp.begin(), temp.end(), obj) != temp.end();
	};
	
	//empty the table
	void makeEmpty(){
		for(int i = 0; i < table.size(); i++){
			table[i].clear();
		}
		currentSize = 0;
	};
	
	//judge whether table is empty
	bool isEmpty(){
		return currentSize == 0;
	};
	
	//insert obj into table
	bool insert(const T &obj){
		list<T> &temp = table[myhash(obj)];
		//judge whether obj exist in table
		if(find(temp.begin(), temp.end(), obj) != temp.end()){
			return false;
		}
		temp.push_back(obj);
		if(++currentSize > table.size()){
			rehash();
		}
		return true;
	};
	
	//remove obj outof table
	bool remove(const T &obj){
		list<T> &temp = table[myhash(obj)];
		typename list<T>::iterator itr = find(temp.begin(), temp.end(), obj);
		if(itr == temp.end()){
			return false;
		}
		temp.erase(itr);
		currentSize--;
		return true;
	};
	
	//print the table
	void print(){
		cout << " the table info as follows : " << endl;
		for(int i = 0; i < table.size(); i++){
			cout << i << " : " << endl;
			for(typename list<T>::iterator itr = table[i].begin(); itr != table[i].end(); ++itr){
				cout << *itr;
			}
		}
	};
private:
	//rehash operation
	void rehash(){
		vector<list<T> > temp = table;
		table.resize(nextPrime(table.size()));
		for(int i = 0; i < table.size(); i++){
			table[i].clear();
		}
		for(int i = 0; i < temp.size(); i++){
			typename list<T>::iterator itr = temp[i].begin();
			while(itr != temp[i].end()){
				insert(*itr);
			}
		}
	};
	
	//hash function
	int myhash(const T &obj) const{
		int hashVal = hash(obj);
		hashVal %= table.size();
		if(hashVal < 0){
			hashVal += table.size();
		}
		return hashVal;
	};
	
	//find next prime while is bigger than the twice of currentSize
	int nextPrime(int n){
		//set flag
		bool flag = true;
		for(int i = 2 * n; ; i++) {
			for(int j = 2;j < i; j++) {
				if(i % j == 0) {
					//not a prime
					flag = false;
					break;
				}
			}
			//find the prime
			if(flag) {
				return i;
				break;
			}
			flag = true;
		}
	};
};

#endif

2、main.cpp

#include<iostream>
#include"hash.h"

using namespace std;

int main(){
	Student stu[6] = {
		Student("Aimi", "python", 80.6),
		Student("Brone", "C++", 90.3),
		Student("Cver", "python", 85.6),
		Student("David", "C++", 89.7),
		Student("Eular", "java", 85.0),
		Student("Frank", "C++", 92.1)
	};
	
	vector<Student> vstu;
	for(int i = 0; i < 6; i++){
		vstu.push_back(stu[i]);
	}
	
	for(int i = 0; i < 6; i++){
		cout << vstu[i];
	}
	
	HashTable<Student> hashTable;
	for(int i = 0; i < 6; i++){
		hashTable.insert(vstu[i]);
	}
	
	cout << "******** print ********" << endl;
	hashTable.print();
	
	cout << "******** contain ********" << endl;	
	if(hashTable.contain(stu[0])){
		cout << " contain " << stu[0] << endl;
	}else{
		cout << "does not exist the object !" << endl;
	}
	
	cout << "******** findElement ********" << endl;
	cout << hashTable.findElement(stu[1]) << endl;
	
	cout << "******** insert ********" << endl;
	hashTable.insert(Student("Furier", "java", 89.0));
	hashTable.print();
	
	cout << "******** remove ********" << endl;	
	hashTable.remove(stu[3]);
	hashTable.print();	
	return 0;
}

practice makes perfect !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值