[C/C++] -- 哈希表

1.简介

哈希表(Hash Table)是一种常用的数据结构,用于实现键值对之间的映射关系。它基于哈希函数(Hash Function)来快速定位和访问数据,具有高效的查找、插入和删除操作。

以下是哈希表的理论基础:

  1. 哈希函数:哈希表的核心在于哈希函数。哈希函数接受一个键(key)作为输入,并将其映射到一个固定大小的整数值,该值称为哈希码(hash code)。好的哈希函数应当具有以下特点:

    • 易于计算:哈希函数应当能够快速计算出哈希码。
    • 均匀分布:哈希函数应当使不同的键均匀地映射到哈希表中的不同位置,以减少冲突。
    • 最小冲突:尽可能避免不同的键映射到相同的哈希码,从而减少冲突。
  2. 解决冲突:由于哈希函数的限制,不同的键可能映射到相同的哈希码,导致冲突。解决冲突的常见方法包括:

    • 链地址法(Chaining):将冲突的键值对存储在同一个位置上,并使用链表、树等数据结构来处理碰撞。
    • 开放寻址法(Open Addressing):在发生冲突时,通过探测序列来寻找下一个可用的位置。
  3. 时间复杂度:哈希表的查找、插入和删除操作的平均时间复杂度为 O(1),即常数时间复杂度。但在最坏情况下,时间复杂度可能会达到 O(n),取决于哈希函数的质量和解决冲突的方法。

  4. 空间利用率:哈希表可以提供高效的空间利用率,因为它根据哈希码直接定位数据的存储位置,而不需要像数组那样按顺序存储。

2.例题

两数之和

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
  • 暴力枚举
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for(int i = 0;i < n;i++){
            for(int j = i + 1;j < n;j++){
                if(nums[i] + nums[j] == target){
                    return {i,j};
                }
            }
        }
        return {};
    }
};
  • 哈希表
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> hashTable;
        for(int i = 0;i < nums.size();i++){
            auto it = hashTable.find(target - nums[i]);
            if(it != hashTable.end()){
                return {it->second,i};
            }
            hashTable[nums[i]] = i;
        }
        return {};
    }
};

最长连续序列

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

要求时间复杂度为0(n)

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> hash;
        for(auto num:nums){
            hash.insert(num);
        }
        int res = 0;
        for(auto num:hash){
            if(!hash.count(num - 1)){
                int y = num;
                while(hash.count(y + 1))
                    y++;
                res = max(res,y-num+1);
            }
        }
        return res;
    }
};

3.总结

  • 算法场景

一般哈希表是用来快速判断一个元素是否出现集合中。

哈希表比起暴力枚举,牺牲了空间,换取了时间。

  • 哈希函数

哈希函数 hashFunction = hashCode(类型) % tableSize

hashCode(类型)是针对特定类型的键计算得到的哈希码,tableSize表示哈希表的大小,%是取模运算符。这个公式的作用是将哈希码对哈希表的大小取模,以确定键应该放置在哈希表的哪个位置。取模运算有助于确保哈希码分布均匀,避免出现哈希冲突(多个键映射到同一个位置)。

  • 哈希碰撞

即使哈希函数运行的再均匀,当键值大于哈希表大小时,仍然会映射到哈希表同一个索引位置。

解决哈希碰撞:拉链法、线性探测法。

拉链法:插入时,如果发生碰撞,新的键值对会被插入到对应桶的链表中。查找时,需要遍历链表来查找对应的键值对。拉链法相对简单且容易实现,适用于大多数情况下哈希碰撞不频繁的场景。

线性探测法:当发生哈希碰撞时,算法会线性地探测下一个可用的空槽,如果目标槽已经被占用,则按照一定的探测序列向后查找空槽,直到找到一个空槽或者遍历完整个哈希表。线性探测法可能会导致聚集(clustering)问题,即连续的槽被占用,影响了查找的性能。为了解决这个问题,可以采用二次探测法、双重散列等方法。

  • 数据结构

哈希表常见的三种数据结构:数组、set(集合)、map(映射)。

集合底层是否有序是否重复能否更改数值查询效率增删效率
set红黑树有序O(log n)O(log n)
multiset红黑树有序O(log n)O(log n)
unordered_set哈希表无序O(1)O(1)
映射底层是否有序是否重复能否更改数值查询效率增删效率
map红黑树有序key否key否O(log n)O(log n)
multimap红黑树有序key是key否O(log n)O(log n)
unordered_map哈希表无序key否key否O(1)O(1)
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值