力扣热题一百题解(C++)-哈希表

题目

1.两数之和

给定一个整数数组 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; // 在此情形下,未找到则返回空

49.字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

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;

128.最长连续序列

给定一个未排序的整数数组 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++实现哈希表

在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容器

‌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)。
修改‌:需要先查询键,再更新值,因此时间复杂度与查询相同‌。

性能优化建议

‌选择高效哈希函数‌:均匀分布键的哈希值,减少冲突。
‌预分配桶数量‌:如果已知元素规模,初始化时指定桶数量可以优化性能。

unordered_map在c++中的用法

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(); // 清空所有元素

你可能感兴趣的:(STL标准库(c++),力扣刷题,散列表,leetcode,c++)