小工具知识网

当前位置: 首页 > 行业知识 > 哈希表C++哈希表详解(知识点+相关LeetCode题目)

行业知识

哈希表C++哈希表详解(知识点+相关LeetCode题目)

2026-11-07 10:01:09 来源:互联网转载

���录

()

前言

一、什么是哈希表

()

二、哈希表的操作

2.1 操作时间复杂度

2.2 哈希表通用API

2.3 建立哈希表

2.3 哈希表常见结构介绍

Set(集合)

Map(映射)

unordered_map(哈希表)

三、哈希表的力扣经典题目

有效的字母异位词242

两个数组的交集 349

两数之和1

四数相加II 454

三数之和 15

四数之和 18


前言

本文将从哈希表的概念、复杂度、STL实现函数、哈希表相关经典题目展开叙述。

一、什么是哈希表

哈希表是散列表,就是通过关键码值而直接进行访问的一种数据结构

哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素

其内部由一个个key:value 样式的键值对组成。

哈希表中的key通过哈希函数得到内存地址,然后将key和value放到对应的内存地址,从而实现通过key获取Value的方式

哈希碰撞:2个不同的key通过哈希函数(hash function)得到了相同的内存地址,也就是是内存地址已经被一个占用了,解决方法是将其中之一变为链表结构,使用next指向。这样内存地址就不会重复,但是会影响查询

二、哈希表的操作

2.1 操作时间复杂度

1) 搜索:1.无哈希碰撞,直接用哈希函数通过Key定位到内存地址,复杂度O(1)                                               2.有哈希碰撞,因为存在内存地址需要通过链表查询,复杂度O(N)

2) 插入:通过key找到内存地址插入即可,复杂度 O(1)--自动插入

unorder_map InquireMap;InquireMap[val] = 1;

3) 删除:通过key找到内存地址删除即可,复杂度 O(1)

2.2 哈希表通用API

  • begin():该函数返回一个指向哈希表开始位置的迭代器
  • end():返回一个指向哈希表结尾位置的下一个元素的迭代器
  • empty():判断哈希表是否为空,空则返回true,非空返回false
  • size():返回哈希表的大小
  • erase(): 删除某个位置的元素,或者删除某个位置开始到某个位置结束这一范围内的元素, 或者传入key值删除键值对
  • at():根据key查找哈希表中的元素
  • clear():清空哈希表中的元素
  • find():以key作为参数寻找哈希表中的元素,如果哈希表中存在该key值则返回该位置上的迭代器,否则返回哈希表最后一个元素下一位置上的迭代器
  • count(): 统计某个key值对应的元素个数 (注:因为unordered_map不允许重复元素,所以返回值为0或1)

    2.3 建立哈希表

    unordered_map correspond{        {'I',1},        {'V',5},        {'X',10},        {'L',50},        {'C',100},        {'D',500},        {'M',1000},    };

    2.3 哈希表常见结构介绍

    总体结构上分为数组、set、map

    数组也是一种意义上的哈希表

    set的结构中每个元素都是一个值(类似于数组)

    map的结构是一个 key:value 的数据结构

    Set(集合)

    1) set、multiset 基于红黑树(一种平衡二叉搜索树),所以(key)值都是有序的,但是不可以修改,一旦修改就会引起整棵树的错乱,所以只能删除和增加,两者唯一的区别是前者不可重复(key)值,后者可以重复

    2)unordered_set 底层实现为哈希表,区别就是没有关键码这个说法,哈希表是无序的,所以查询效率要快些O(1)就可以实现。因为无序,所以值也不可以重复

    小结:当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

    这三种数据结构的具体优劣在这里引用一张表来直观体现

    集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
    std::set红黑树有序否否O(log n)O(log n)
    std::multiset红黑树有序是否O(logn)O(logn)
    std::unordered_set哈希表无序否否O(1)O(1)

    Map(映射)

    1)map、multimap  基于红黑树(一种平衡二叉搜索树),key是有序的,和上面提到的set、multimap一样,key值可以添加、删除,但是不可以更改(map中,对key是有限制,对value没有限制的。所以说value值可以修改)两者唯一的区别是前者不可重复key值,后者可以重复

    2)unordered_map 底层实现为哈希表(无序),因为无序,所以key也不可以重复

    映射底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
    std::map红黑树key有序key不可重复key不可修改O(logn)O(logn)
    std::multimap红黑树key有序key可重复key不可修改O(log n)O(log n)
    std::unordered_map哈希表key无序key不可重复key不可修改O(1)

    O(1)

    unordered_map(哈希表)

    这个数据结构更加常用,所以在这里特别讲一下它的性质

    • unordered_map底层存储的是键值对,可以通过key快速的索引到value unordered_map内部因为是数据是通过映射存入哈希桶内的,所以对unordered_map进行遍历得到的是一个无序的序列。
    • unordered_map通过key进行访问的速度比map快,但是遍历元素的迭代效率就要低于map了。unordered_map也实现了operator[ ] 可以通过key直接访问到value

      三、哈希表的力扣经典题目

      有效的字母异位词242

      给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

      注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

      示例 1:

      输入: s = "anagram", t = "nagaram"

      输出: true

      class Solution {public:    bool isAnagram(string s, string t) {        int hash[26] = {0};//26个英文字符        for(int i =0 ;i left && nums[right] == nums[right-1]){                        right--;                    }                    while(right > left && nums[left] == nums[left+1]){                        left++;                    }                                        //当去完重之后进行原来的操作--收缩指针--原先的都已经用过了,已经为恰好满足                    right--;                    left++;                }            }        }        return result;    }};

      题解:

      整体上的思路是用双指针替代了哈希表的算法。因为需要返回一个二维数组,并且包括每一种可能,并且还要不重复,所以在这里使用双指针去重的思路。

      排序整个数组,从不重复的数字开始,作为i元素,之后在后面的区间里设置j和k两个元素在其各自去重的情况下,计算和是否满足为0,j和k两个元素作为双指针从i+1的位置和size-1的位置向中心移动,移动时检测去重,最终返回结果

      四数之和 18

      给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

      0 =0){ //剪枝处理--便于执行(只大于不等于--因为等于可能也是一种解法 target 0 0 0) //因为只要求k,i,left,right互不相同就可以,去重也是为了防止元素间的对应,就比如当前这个元素使用过了,就不能再使用,两个是分开的意思 break; } if(k>0 && nums[k]==nums[k-1]){ continue;//去重 } for(int i=k+1;i target && nums[i]+nums[k] >= 0){ //剪枝处理的原理是当前的元素大于target,并且两元素大于0,说明后面不可能成立了(都大于0) break; } //去重一定是从k之后元素开始算,因为这也是i的范围 if(i>k+1 && nums[i] == nums[i-1]){ continue; } int left = i+1; int right = nums.size()-1; while(left target){ right--; }else if(targetNum

上一篇:音响怎么接服务器的线?

下一篇:从零开始DIY织梦模板,打造个性化网站模板的实用教程!