给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
unordered_map<int, int> num_map; // 哈希表,用于存储元素及其索引
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i]; // 计算当前元素的补数
// 检查补数是否在哈希表中
if (num_map.find(complement) != num_map.end()) {
result.push_back(num_map[complement]); // 添加补数的索引
result.push_back(i); // 添加当前元素的索引
return result; // 返回结果
}
// 将当前元素和它的索引存入哈希表
num_map[nums[i]] = i;
}
return result; // 在此情形下,未找到则返回空
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
unordered_map<string, vector<string>> anagramMap;
for (const string& str : strs) {
// 对字符串进行排序,作为字母异位词的键
string sortedStr = str;
sort(sortedStr.begin(), sortedStr.end());
// 将原字符串放入对应的键的向量中
anagramMap[sortedStr].push_back(str);
}
// 将结果从哈希表转换成向量
vector<vector<string>> result;
for (const auto& pair : anagramMap) {
result.push_back(pair.second);
}
return result;
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
unordered_set<int> numSet(nums.begin(), nums.end()); // 将数组元素存入集合
int longestStreak = 0; // 初始化最长序列的长度
for (const int& num : numSet) {
// 只从序列的开始数字开始计算
if (numSet.find(num - 1) == numSet.end()) {
int currentNum = num;
int currentStreak = 1;
// 向上查找连续数字
while (numSet.find(currentNum + 1) != numSet.end()) {
currentNum++;
currentStreak++;
}
// 更新最长序列
longestStreak = max(longestStreak, currentStreak);
}
}
return longestStreak;
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数,按这个思想建立的表为散列表。
对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)==f(k2),这种现象称为冲突(英语:Collision)。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数f(k)和处理冲突的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为散列表,这一映射过程称为散列造表或散列,所得的存储位置称散列地址。
若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。
在C++中,实现哈希表有多种方式,其中最常见的是使用标准库中的std::unordered_map。std::unordered_map是基于哈希表的实现,提供了快速的插入、查找和删除操作。但是,如果你想从头开始实现一个简单的哈希表,你可以使用数组(或向量)来存储数据,并使用哈希函数来确定每个元素应该存储的位置。
下面是一个简单的自定义哈希表实现的例子,使用链地址法解决哈希冲突:
1.定义节点结构
首先,定义一个链表节点,用于存储键值对和解决哈希冲突。
#include
#include
#include
#include // For std::hash
template<typename K, typename V>
struct HashNode {
K key;
V value;
HashNode* next; // Pointer to handle collisions
HashNode(K k, V v) : key(k), value(v), next(nullptr) {}
};
2. 哈希表类定义
然后,定义一个哈希表类,包含插入、查找和删除操作。
template<typename K, typename V>
class SimpleHashTable {
private:
std::vector<HashNode<K, V>*> table; // 数组用于存储链表的头节点
size_t capacity; // 哈希表容量
std::hash<K> hashFunction; // 默认的哈希函数
size_t hash(const K& key) const {
return hashFunction(key) % capacity; // 计算哈希值并取模以确定位置
}
public:
SimpleHashTable(size_t size = 10) : capacity(size) {
table.resize(capacity, nullptr); // 初始化数组为nullptr,表示空链表头
}
~SimpleHashTable() {
// 清理所有节点,防止内存泄漏
for (auto& node : table) {
HashNode<K, V>* current = node;
while (current) {
HashNode<K, V>* temp = current;
current = current->next;
delete temp;
}
}
}
void insert(const K& key, const V& value) {
size_t index = hash(key); // 计算索引位置
HashNode<K, V>* newNode = new HashNode<K, V>(key, value); // 创建新节点
newNode->next = table[index]; // 将新节点插入到链表头部
table[index] = newNode; // 更新数组中的头节点指针
}
bool find(const K& key, V& value) const {
size_t index = hash(key); // 计算索引位置
HashNode<K, V>* current = table[index]; // 获取链表的头节点
while (current) {
if (current->key == key) { // 找到匹配的键
value = current->value; // 获取值并返回true表示找到
return true;
}
current = current->next; // 继续搜索链表中的下一个节点
}
return false; // 未找到键,返回false
}
bool remove(const K& key) {
size_t index = hash(key); // 计算索引位置
HashNode<K, V>** head = &table[index]; // 获取链表头节点的地址以便修改指针
while (*head) { // 遍历链表直到找到键或到达链表末尾
if ((*head)->key == key) { // 找到键,进行删除操作
HashNode<K, V>* temp = *head; // 保存要删除的节点指针
*head = (*head)->next; // 更新头节点指针,跳过要删除的节点
delete temp; // 释放内存资源
return true; // 删除成功返回true
}
head = &((*head)->next); // 移动到下一个节点继续搜索或删除操作的位置上(如果需要的话)
}
return false; // 未找到键,返回false表示删除失败(即键不存在)
}
};
unordered_map是C++标准模板库(STL)中的一个关联容器,基于哈希表实现,用于存储键值对(key-value pairs)。其核心特点是快速查找、插入和删除操作,平均时间复杂度为O(1),但在最坏情况下(如哈希冲突严重时)可能会退化为O(n)
。
特性
无序性:unordered_map不保证元素的顺序,与基于红黑树的map不同,后者是有序的。
高效性:在平均情况下,插入、查询和删除操作的时间复杂度为O(1),但在最坏情况下可能退化为O(n)。
键唯一性:每个键是唯一的,重复插入会覆盖原有值。
底层实现原理
unordered_map的底层结构由哈希函数和哈希桶组成。哈希函数将键转换为哈希值,决定键值对存储的桶位置。当多个键映射到同一桶时,采用链地址法解决冲突,即所有冲突的键值对存储在同一链表中。当元素数量超过负载因子时,哈希表会自动扩容并重新哈希所有元素,这个过程的时间复杂度为O(n),但均摊到每次操作仍为O(1)。
操作复杂度
查询:平均时间复杂度为O(1),最坏情况下为O(n)。
插入:平均时间复杂度为O(1),最坏情况下为O(n)。
删除:平均时间复杂度为O(1),最坏情况下为O(n)。
修改:需要先查询键,再更新值,因此时间复杂度与查询相同。
性能优化建议
选择高效哈希函数:均匀分布键的哈希值,减少冲突。
预分配桶数量:如果已知元素规模,初始化时指定桶数量可以优化性能。
1. 引入头文件
#include
2. 声明 unordered_map
unordered_map<int,string>myMap//键为it,值为string
3. 插入元素
myMap.insert({1,"orange"});
4. 查找元素
可以使用 find 方法和 [] 操作符来查找元素:
auto it=myMap.find(1);
if(it!=myMap.end())
{
cout<<it->second<<endl;
}
5. 删除元素
使用 erase 方法来删除元素:
myMap.erase(1); // 删除键为 1 的元素
6. 遍历 unordered_map
使用范围-based for 循环遍历 unordered_map:
for (const auto& pair : myMap)
{
cout << "Key: " << pair.first << ", Value: " << pair.second << endl;
}
7. 检查键的存在性
使用 count 方法可以检查特定键是否存在:
if (myMap.count(2) > 0)
{
cout << "Key 2 exists." << endl;
} else
{
cout << "Key 2 does not exist." << endl;
}
8. 大小和清空
获取 unordered_map 的大小和清空内容:
cout << "Size: " << myMap.size() << endl;
myMap.clear(); // 清空所有元素