哈希表(cpp)

目录

哈希表的实现

 哈希函数(将不同类型的键值key,映射到哈希表索引范围内的整数,即:index = H( key ) )

1. 直接定址法

2. 平方取中法

3. 折叠法

4. 除留余数法

5. 位掩码法(除数留余法的特殊优化)

 标准库中的哈希函数

 哈希表的代码实现(采用链地址法解决哈希冲突) 

标准库中的哈希表

内置函数


哈希表的实现

 哈希函数(将不同类型的键值key,映射到哈希表索引范围内的整数,即:index = H( key ) )

1. 直接定址法

  • 映射函数:H(key) = key 或 H(key) = a * key + b(其中a和b为常数)。
  • 作用的键的类型:直接定址法适用于整数类型的键,或者可以简单转换为整数的键(如通过类型转换或字符串哈希)。这种方法简单直接,但要求事先知道关键字的分布情况,适合查找表较小且连续的情况。

2. 平方取中法

  • 映射函数:取关键字的平方值的中间几位作为哈希地址。例如,如果关键字是1234,那么它的平方是1522756,然后取中间的3位(如227)作为哈希地址。
  • 作用的键的类型:平方取中法适用于整数类型的键,特别是当关键字的位数适中,且分布较为均匀时。这种方法比较适合于不知道关键字的分布,而位数又不是很大的情况。

3. 折叠法

  • 映射函数:将关键字分割成位数相同的几部分(最后一部分位数可以不同),然后取这几部分的叠加和(注意去除进位)作为哈希地址。折叠法有移位叠加和间界叠加两种方法。
  • 作用的键的类型:折叠法适用于任何可以分割并计算叠加和的键类型,包括整数、字符串等。通过调整分割的位数和叠加的方式,可以灵活处理不同长度的键。

4. 除留余数法

  • 映射函数:H(key) = key mod p。其中,p是一个不大于哈希表大小(size)的最大质数,或者是不包含小于20质因子的合数。
  • 作用的键的类型:除留余数法适用于整数类型的键,也适用于可以通过某种方式转换为整数的键(如字符串哈希)。这种方法通过取模运算将关键字映射到哈希表的有限范围内,是哈希表中最常用的方法之一。而且这种方法也可以运用在折叠法、平方取中法之后。

5. 位掩码法(除数留余法的特殊优化)

  • 映射函数:H(key) = key & (S - 1)。其中,& 表示按位与操作,S是一个合适的2的k次幂,用位运算优化取模运算(此时有 key % S = key & (S - 1) )。
  • 作用的键的类型:由于其是除数留余法的特殊优化,故两者作用类型基本一致。其中整数能够直接进行位运算,并且在哈希表大小为2的幂的情况时表现出色

 标准库中的哈希函数

在C++标准库中,std::hash 是一个模板类,用于生成对象的哈希值。这个类在 <functional> 头文件中定义,并且它为多种基本数据类型和某些标准库类型(如 std::string)提供了特化版本。
使用方法如下:

hash<KeyType> hasher;
size_t index = hasher(key);

// 当类型为自定义类型时
// 一个简单的自定义类型  
struct MyType {  
    int value;    
};  
  
// 为 MyType 提供 std::hash 的特化  
namespace std {  
    template<>  
    struct hash<MyType> {  
        size_t operator()(const MyType& t) const {  
            // 这里使用简单的哈希函数,实际应用中可能需要更复杂的算法  
            return hash<int>()(t.value);  
        }  
    };  
} 

 哈希表的代码实现(采用链地址法解决哈希冲突) 

// 哈希表节点类
template<typename KeyType, typename ValueType>
class HashNode {
public:
	KeyType key; // 键
	ValueType value; // 值
	HashNode *next; // 指向下一个节点

	// 构造函数
	HashNode(KeyType key, ValueType value) {
		this->key = key;
		this->value = value;
		this->next = NULL;
	}
};

// 哈希表类
template<typename KeyType, typename ValueType>
class HashTable {
private:
	int size; // 哈希表大小
	HashNode<KeyType, ValueType> **table; // 哈希表
	// 哈希函数:根据键值key计算哈希地址
	int hash(const KeyType &key) const {
		int hashkey = key % size;
		if(hashkey < 0) hashkey += size;
		return hashkey;
	}
public:
	// 构造函数
	HashTable(int size);
	// 析构函数
	~HashTable();
	// 插入键值对
	void insert(const KeyType &key, const ValueType &value);
	// 删除键值对
	void remove(const KeyType &key);
	// 查找指定键的值
	bool find(const KeyType &key, ValueType &value) const;
};

template<typename KeyType, typename ValueType>
HashTable<KeyType, ValueType>::HashTable(int size) {
	this->size = size;
	table = new HashNode<KeyType, ValueType>*[size];
	for(int i=0; i < size; i++) {
		table[i] = NULL;
	}
}

template<typename KeyType, typename ValueType>
HashTable<KeyType, ValueType>::~HashTable() {
	for(int i=0; i < size; i++) {
		if(table[i]){
			HashNode<KeyType, ValueType> *temp = table[i];
			while(temp) {
				HashNode<KeyType, ValueType> *next = temp->next;
				delete temp;
				temp = next;
			}
		}
	}
	delete[] table;
}

template<typename KeyType, typename ValueType>
void HashTable<KeyType, ValueType>::insert(const KeyType &key, const ValueType &value) {
	int index = hash(key); // 哈希地址
	HashNode<KeyType, ValueType> *node = new HashNode<KeyType, ValueType>(key, value);
	if(table[index] == NULL) {
		table[index] = node;
	} else {
		HashNode<KeyType, ValueType> *temp = table[index];
		while(temp->next) {
			if(temp->key == key) { // 键值对已存在,更新值
				temp->value = value;
				return;
			}
			temp = temp->next;
		}
		// 键值对不存在,插入到链表末尾,处理哈希冲突
		temp->next = node;
	}
}

template<typename KeyType, typename ValueType>
void HashTable<KeyType, ValueType>::remove(const KeyType &key) {
	int index = hash(key); // 哈希地址
	if(table[index] == NULL) {
		return; // 键值key不存在,直接返回
	} else {
		HashNode<KeyType, ValueType> *temp = table[index];
		while(temp->next && temp->next->key != key) temp = temp->next;
		if(temp->next) {
			HashNode<KeyType, ValueType> *next = temp->next->next;
			delete temp->next;
			temp->next = next;
		} else {
			return;
		}
	}
}

template<typename KeyType, typename ValueType>
bool HashTable<KeyType, ValueType>::find(const KeyType &key, ValueType &value) const {
	int index = hash(key); // 哈希地址
	if(table[index] == NULL) {
		return false; // 键值key不存在,直接返回false
	} else {
		if(table[index]->key == key) {
			value = table[index]->value;
			return true; // 键值对存在,返回true
		}
		HashNode<KeyType, ValueType> *temp = table[index]->next;
		while(temp->next && temp->key != key) temp = temp->next;
		if(temp->next) {
			value = temp->next->value;
			return true; // 键值对存在,返回true
		}
	}
	return false; // 键值key不存在,返回false
}

标准库中的哈希表

unordered_map 在 <unordered_map> 头文件中。 unordered_map 通过一个哈希函数来组织数据,使得插入、删除和查找操作在平均情况下具有常数时间复杂度,即 O(1)。不过,在最坏情况下(比如所有键的哈希值都相同),这些操作的时间复杂度会退化到 O(n)。 

// 定义一个哈希表
unordered_map<KeyType, ValueType> Hash;

// 每个键值对是以 std::pair 类型进行存储的,可以如下遍历
for (const auto& pair : Hash) {
    // 这里的 pair 就是 std::pair 类型
    cout << pair.first << " : " << pair.second << '\n';
}

内置函数

unordered_map 提供了丰富的成员函数来支持其操作,包括但不限于:

  • insert:插入一个键值对或键值对数组。
  • emplace:在容器内部直接构造一个键值对(可能更高效)。
  • erase:删除一个元素(通过键、迭代器或迭代器范围)。
  • find:查找一个键,如果找到则返回指向该键的迭代器,否则返回 end() 迭代器。
  • count:返回与指定键相匹配的元素数量(对于 unordered_map,这总是 0 或 1)。
  • at:通过键访问值,如果键不存在则抛出 std::out_of_range 异常。
  • operator[]:通过键访问值,如果键不存在则默认构造一个值(对于内置类型如 int,则是 0)。
  • clear:删除所有元素。
  • size:返回容器中元素的数量。
  • empty:检查容器是否为空。
  • begin/end:返回指向容器中第一个元素和最后一个元素之后位置的迭代器。
// insert 用法
Hash.insert({1, "One"});
Hash.insert(make_pair(2, "Two"));

// emplace 用法
Hash.emplace(3, "Three");

// erase 用法(通过键)
Hash.erase(1);

// erase 用法(通过迭代器)
auto it = Hash.find(2);
if (it!= Hash.end()) {
    Hash.erase(it);
}

// find 用法
auto foundIt = Hash.find(3);
if (foundIt!= Hash.end()) {
    cout << "Found: " << foundIt->second << '\n';
} else {
    cout << "Not found" << '\n';
}

// count 用法
int count = Hash.count(3);
cout << "Count of key 3: " << count << '\n';

// at 用法
try {
    cout << "Value at key 3: " << Hash.at(3) << endl;
} catch (const out_of_range &e) {
    cout << "Key not found" << '\n';
}

// operator[] 用法
Hash[4] = "Four";
cout << "Value at key 4: " << Hash[4] << '\n';

// clear 用法
Hash.clear();

// size 用法
cout << "Size after clear: " << Hash.size() << '\n';

// empty 用法
if (Hash.empty()) {
    cout << "HashMap is empty" << '\n';
} else {
    cout << "HashMap is not empty" << '\n';
}

// begin/end 用法
for (auto it = Hash.begin(); it!= Hash.end(); ++it) {
    cout << it->first << " : " << it->second << '\n';
}

  • 25
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值