数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

开放定址法:  

     在上一篇数据结构与算法分析-C++描述 第5章 散列ADT(分离链接法)中提出散列的基本思想、散列函数以及解决冲突的两种方法:分离链接法和开放定址法,完成了分离链接法的实例应用,本篇承接上篇,实现开放定址法的实例应用。

    分离链接法的缺点是使用了一些链表,由于给新单元分配地址需要时间(特别是在高级语言中),因此这导致了算法速度的下降,同时算法还要求第二种数据结构的实现。使用链表解决冲突的另一个方法是当冲突发生时尝试选择另一个存储单元,直到找到空的单元。更正式的,数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法),依次进行尝试,其中数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法),函数数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)成为冲突解决函数。开放定址法法是不使用链表的散列实现,通常通常装填因子数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法),这样的表称为探测散列表(probing hash tables),探测散列表解决冲突的常见方法:线性探测、平方探测、双散列。

线性探测:数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

平方探测: 数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

双散列:数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

数据结构与算法分析-C++描述 第5章 散列ADT(开放定址法)

注:

     1)探测散列表的删除是懒惰删除(只是简单的标记要删除的节点是为已删除DELETED,然后在查找等例程里面都人为的忽视标记过的节点. ,虽然这种情况下并不存在懒惰的意味;

     2)定理:如果使用平方探测,且表的大小是素数,那么当表至少有一半为空时,总能够插入一个新的元素【反证法】;

     3)如果双散列正确实现,预期的探测次数和随机冲突解决的情形相同,这使得双散列理论上很有吸引力。然而,平方探测不需要使用第二个散列函数,从而实践中可能更简单且快速,当键为字符串时其散列函数的计算将更加耗时;

 实例:建立开放定址法的散列表,完成插入、删除、再散列、查询、清除等功能。

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{
public:
	//define the EntryType 
	enum EntryType{ACTIVE, EMPTY, DELETED};
private:
	struct Entry{
		T element;
		EntryType info;
		//Entry constructor
		Entry(const T &obj = T(), EntryType i = EMPTY):element(obj), info(i){
		}
	};	
	
	vector<Entry> table;
	int currentSize;
public:
	//HashTable constructor
	HashTable(int num = 7):table(nextPrime(num)), currentSize(0){
		makeEmpty();
	};
	
	//HashTable destructor
	~HashTable(){
	};
	
	//judge whether the table contain obj return T obj
	T findElement(const T &obj){
		int currentPos = findPos(obj);
		if(isActive(currentPos)){
			return table[currentPos].element;
		}else{
			T temp;
			return temp;
		}
	};
	
	//find the position of obj
	int findPos(const T &obj){
		/*
			linear prober : f(i) = i, delta = 1;
			square prober : f(i) = i * i, delte = 2 * i - 1;
			double hash : f(i) = i * hash2(x), 
						  hash2(x) = R - (x % R) whether R is prime and R < currentSize 
						  delta = hash2(x)
		*/
		int offSet = 1;
		int currentPos = myhash(obj);
		while((table[currentPos].info != EMPTY) && (table[currentPos].element != obj)){
			//linear prober currentPos += offSet;
			//square prober 
			//currentPos += 2 * offSet - 1;
			//double hash
			currentPos = prePrime(table.size()) - currentPos % prePrime(table.size());
			offSet++;
		}
		if(currentPos >= table.size()){
			currentPos -=  table.size();
		}
		return currentPos;
	};
	
	//judge whether table contain obj reutrn bool
	bool contain(const T &obj){
		return isActive(findPos(obj));
	};
	
	//empty the table
	void makeEmpty(){
		for(int i = 0; i < table.size(); i++){
			table[i].info = EMPTY;
		}
		currentSize = 0;
	};
	
	//judge whether table is empty
	bool isEmpty(){
		return currentSize == 0;
	};
	
	//insert obj into table
	bool insert(const T &obj){
		int currentPos = findPos(obj);
		if(isActive(currentPos)){
			return false;
		}
		table[currentPos] = Entry(obj, ACTIVE);
		if(++currentSize >= table.size() / 2){
			rehash();
		}
		return true;
	};
	
	//remove obj outof table
	bool remove(const T &obj){
		int currentPos = findPos(obj);
		if(!isActive(currentPos)){
			return false;
		}
		table[currentPos].info = DELETED;
		currentSize--;
		return true;
	};
	
	//print the table
	void print(){
		cout << " the table info as follows : " << endl;
		for(int i = 0; i < table.size(); i++){
			if(isActive(i)){
				cout << i << " : " << table[i] << endl;
			}
		}
	};
	
	friend ostream & operator<<(ostream &os, const Entry &e){
		os << "element : " << e.element << setw(8) << " info : " << e.info << endl;
		return os; 
	};
	
private:
	//rehash operation
	void rehash(){
		vector<Entry> temp = table;
		table.resize(nextPrime(2 * table.size()));
		for(int i = 0; i < table.size(); i++){
			table[i].info = EMPTY;
		}
		currentSize = 0;
		for(int i = 0; i < temp.size(); i++){
			if(temp[i].info == ACTIVE){
				insert(temp[i].element);
			}
		}
	};
	
	//hash function
	int myhash(const T &obj) const{
		int hashVal = hash(obj);
		hashVal %= table.size();
		if(hashVal < 0){
			hashVal += table.size();
		}
		return hashVal;
	};
	
	//judge whether the obj at currentPos is actived or not 
	bool isActive(int currentPos) const{
		return table[currentPos].info == ACTIVE;
	};
	
	//find next prime which is bigger than currentSize
	int nextPrime(int n){
		//set flag
		bool flag = true;
		for(int i = 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;
		}
	};
	
	//find previous prime which is smaller than currentSize
	int prePrime(int n){
		//set flag
		bool flag = true;
		for(int i = n; i > 1; i--) {
			for(int j = i - 1;j  > 1; 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

//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];
	}
	
	cout << "******** insert ********" << endl;
	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 !

参考博客: 数据结构与算法——散列表类的C++实现(探测散列表)