数据结构(五):哈希表及面试常考的算法

这篇具有很好参考价值的文章主要介绍了数据结构(五):哈希表及面试常考的算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、哈希表介绍

1、定义

哈希表,也叫散列表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。例如,下列键(key)为人名,value为性别。

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

2、常用的哈希结构

数组

map(映射)

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

set(集合)

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

2、优缺点及使用场景

优点:高效的查找和插入操作、适用于大数据量、灵活性、快速的删除操作

缺点:空间消耗、不适合有序数据、哈希冲突、依赖好的哈希函数

使用场景:快速查找需求、缓存实现、消除重复元素、分布式系统

3、决定哈希表结构的性能的三个因素

哈希函数、哈希表的大小、碰撞处理方法。

4、基本操作

数据存储:假设我们需要存储5个元素,首先使用哈希函数(Hash)计算Joe的键,也就是字符串‘Joe’的哈希值,得到4928,然后将哈希值除以数组长度5(mod运算),求得其余数。因此,我们将Joe的数据存进数组的3号箱子中。

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

冲突:如果两个哈希值取余的结果相同,我们称这种情况为‘冲突’。假设Nell键的哈希值为6276,mod 5的结果为1。但此时1号箱已经存储了Sue的数据,可使用链表在已有的数据的后面继续存储新的数据。(本方法为链地址法,还有几种解决冲突的方法。其中,应用较为广泛的是“开放地址法”)。

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

查询:假设最终的哈希表为

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

如果要查找Ally的性别,首先算出Alley键的哈希值,然后对它进行mod运算。最终结果为3。

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

然而3号箱中数据的键是Joe而不是Ally。此时便需要对Joe所在的链表进行线性查找。找到了键为Ally的数据。取出其对应的值,便知道了Ally的性别为女(F)。 

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

二、面试常考的算法

1、在数组中查找对称键值对

题目:给定一个整数对数组,找到所有对称对,即相互镜像的对。

示例:

Input:  {3, 4}, {1, 2}, {5, 2}, {7, 10}, {4, 3}, {2, 5}
Output:{4, 3} | {3, 4} ,{2, 5} | {5, 2}

思路:使用一个哈希表map,将数组中第一个元素作为键,第二个元素作为值。遍历数组中的每对元素,对于每对元素,检查反向对是否已经存在于哈希表中。如果存在,说明找到了对称键值对,否则,将当前对插入到哈希表中。

map.find(2)  //查找key为2的键值对是否存在 ,若没找到则返回map.end()。

map.end()   //指向哈希表的最后一个容器,实则超出了哈希表的范围,为空。

#include<iostream>
#include<map>
#include<unordered_map>
using namespace std;

// 查找对称键值对
void has_symmetric_pair(pair<int, int> intPairArray[], int length){
    unordered_map<int, int> map;
    for(int i = 0; i < length; i++){
       int key = intPairArray[i].first;
       int value = intPairArray[i].second;
       
    // 检查反向对是否存在于哈希表中 
    if (map.find(value) != map.end() && map[value] == key){
        cout << "对称键值对有:(" << key <<", " << value<< ")|" <<"(" << value << ", " <<key <<")\n";
    }
    // 将当前对插入哈希表
    map[key] = value;
    }
}


int main() {
    // 创建一个整数对数组
    pair<int, int> intPairArray[6];
    int length = 6;
    // 分别给数组的元素赋值
    intPairArray[0] = make_pair(1, 2);
    intPairArray[1] = make_pair(3, 4);
    intPairArray[2] = make_pair(5, 6);
    intPairArray[3] = make_pair(7, 8);
    intPairArray[4] = make_pair(6, 5);
    intPairArray[5] = make_pair(4, 3);

    // 访问数组的元素
    for (int i = 0; i < 6; ++i) {
        std::cout << "Element " << i << ": (" << intPairArray[i].first << ", " << intPairArray[i].second << ")\n";
    }
    has_symmetric_pair(intPairArray, length);
    return 0;
}

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

2、追踪遍历的完整路径

题目:使用哈希实现树的遍历路径,输出每个叶子节点的路径。

示例:input:给定一颗树

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

           output:Node 4 Path: 1 -> 2 -> 4 ->

                         Node 5 Path: 1 -> 2 -> 5 ->

                         Node 3 Path: 1 -> 3 ->

思路:在DFS的先序遍历中,逐步构建路径,当遇到叶子节点时,将该节点和对应的路径存储在哈希表中。最后,遍历哈希表输出结果。

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

unordered_map<TreeNode*, vector<int>> pathMap; 

void dfs(TreeNode* node, vector<int>& path) {
    if (node == nullptr) return;

    path.push_back(node->val);

    if (node->left == nullptr && node->right == nullptr) {
        pathMap[node] = path; 
    } else {
        dfs(node->left, path);
        dfs(node->right, path);
    }

    path.pop_back(); 
}

int main() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    vector<int> path;
    dfs(root, path);

    for (auto pair : pathMap) {
        cout << "Node " << pair.first->val << " Path: ";
        for (int val : pair.second) {
            cout << val << " -> ";
        }
        cout << endl;
    }

    delete root->left->right;
    delete root->left->left;
    delete root->left;
    delete root->right;
    delete root;

    return 0;
}

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

3、查找数组是否是另一个数组的子集

题目:输入两个数组,如果数组1是数组2的元素,则返回True,否则返回False。

示例:input:数组1[3,2,4],数组2[1,2,3,4,5,8]     output:数组1是数组2的子集

思路:先将两个数组转换为 set,然后通过遍历第一个集合,检查其中的每个元素是否也在第二个集合中。

if(set2.find(num) != set2.end())   //判断找到了key为2的键值对

#include<set>
#include<iostream>
using namespace std;

bool isSubSet(int nums1[], int nums2[], int length1, int length2){
    set<int> set1, set2;
    // int length1 = sizeof(nums1) / sizeof(nums1[0]);
    // int length2 = sizeof(nums2) / sizeof(nums2[0]);
    for(int i = 0; i < length1; i++){
        set1.insert(nums1[i]);
    }
    
    for(int j = 0; j < length2; j++){
        set2.insert(nums2[j]);
    }
    for(int num: set1){
        if(set2.find(num) == set2.end()){
            return false;
        }
    }
    return true;
}

int main(){
    int nums1[] = {3, 2, 4};
    int nums2[] = {1, 2, 3, 4, 5, 8};
    int length1 = sizeof(nums1) / sizeof(nums1[0]);
    int length2 = sizeof(nums2) / sizeof(nums2[0]);
    bool result = isSubSet(nums1,nums2, length1, length2);
    if (result) {
        cout << "arr1 is a subset of arr2" << std::endl;
    } else {
        cout << "arr1 is not a subset of arr2" << std::endl;
    }

}

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试

4、检查给定的数组是否不相交

题目:输入两个数组,如果数组1与数组2相交,则返回True,否则返回False。

示例:input:数组1[9],数组2[3,2]     output:数组1与数组2不相交。

思路:先将两个数组转换为set,然后使用set_intersection 函数找到它们的交集。如果交集为空,则数组不相交。

std::set_intersection 是 C++ 标准库 <algorithm> 头文件中的一个函数,它用于求两个已排序容器(比如集合或数组)的交集。

函数声明如下:

template<class InputIt1, class InputIt2, class OutputIt>
OutputIt set_intersection(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first);

  • first1, last1: 第一个容器的起始和结束迭代器。
  • first2, last2: 第二个容器的起始和结束迭代器。
  • d_first: 结果输出的目标容器的起始迭代器。
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
bool areDisjoint(int arr1[], int arr2[], int length1, int length2){
    set<int> set1, set2;
    for(int i = 0; i < length1; i++){
        set1.insert(arr1[i]);
    }
    
    for(int j = 0; j < length2; j++){
        set2.insert(arr2[j]);
    }
    // 同检查数组是否是另一个数组的子集
    // for(auto c: set1){
    //     if(set2.find(c) != set2.end()){
    //         return true;
    //     }
    // }
    // return false;
    
    // 利用集合的交集来求
    set<int> intersection;
    set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.begin()));
    if (intersection.empty()){
        return false;
    }
    return true;
}

int main(){
    int arr1[] = {9};
    int arr2[] = {3, 2};
    int length1 = sizeof(arr1) / sizeof(arr1[0]);
    int length2 = sizeof(arr2) / sizeof(arr2[0]);
    bool res = areDisjoint(arr1, arr2, length1, length2);
    if (res){
        cout << "arr1与arr2相交";
    }
    else{
        cout << "arr1与arr2不相交";
    }
}

数据结构(五):哈希表及面试常考的算法,数据结构,c++,数据结构,散列表,面试文章来源地址https://www.toymoban.com/news/detail-745733.html

到了这里,关于数据结构(五):哈希表及面试常考的算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 10个常考的前端手写题,你全都会吗?

    前言  📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!  🍅  个人主页: 南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、bind 实现call 实现apply 实现bind 3.防抖和节流 防抖 节流 4.实现instanceof 5.实现Ajax 6.深拷贝

    2024年01月22日
    浏览(47)
  • 10个常考的前端手写题,你全都会吗?(上)

    前言  📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!  🍅  个人主页: 南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、bind 实现call 实现apply 实现bind 3.防抖和节流 防抖 节流 4.实现instanceof 5.实现Ajax 6.深拷贝

    2024年01月25日
    浏览(44)
  • 10个常考的前端手写题,你全都会吗?(下)

    前言  📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!  🍅  个人主页: 南木元元 今天接着上篇再来分享一下10个常见的JavaScript手写功能。 目录 1.实现继承 ES5继承(寄生组合式继承) ES6继承 2.获取URL参数 split方法 URLSearchParams方法 3.手写red

    2024年01月25日
    浏览(36)
  • 数据结构-哈希-哈希表实现

    🚀理想的搜索方法:不经过任何的比较,一次直接从表中得到要搜索的元素。如果构造一种存储结构,通过某种函数使元素的存储位置与其关键码之间能够建立起一一映射的关系,那么在查找的时候就能通过此函数快速的找到该元素。 🚀向该结构中插入元素:根据该元素的

    2024年02月10日
    浏览(36)
  • 【数据结构】哈希底层结构

    目录 一、哈希概念 二、哈希实现 1、闭散列 1.1、线性探测 1.2、二次探测 2、开散列 2.1、开散列的概念 2.2、开散列的结构 2.3、开散列的查找 2.4、开散列的插入 2.5、开散列的删除 3、性能分析  顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查

    2024年02月06日
    浏览(45)
  • 【数据结构】哈希表与哈希桶

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.概念 2.哈希冲突 3.解决哈希冲突 3.1闭散列 3.2开散列(哈希桶) 4.模拟实

    2024年03月21日
    浏览(44)
  • 「数据结构」哈希表2:实现哈希表

    🎇 个人主页 :Ice_Sugar_7 🎇 所属专栏 :Java数据结构 🎇 欢迎点赞收藏加关注哦! 在讲插入之前需要先了解扩容,因为 插入后载荷因子如果超过阈值,那我们就要扩容,即扩容是插入操作的一部分 扩容后,原先哈希表中的元素的哈希地址会改变。之前会发生哈希冲突的元素

    2024年02月21日
    浏览(39)
  • 【数据结构】哈希表——闭散列 | 开散列(哈希桶)

    🐱作者:一只大喵咪1201 🐱专栏:《数据结构与算法》 🔥格言: 你只管努力,剩下的交给时间! 哈希(Hash):是一种方法,将数据的key值和存储位置建立关系。 在之前学习过的顺序结构以及平衡树中,所有数据的key值和存储位置之间都没有对应的关系。所以在查找一个数据

    2023年04月24日
    浏览(45)
  • 数据结构——哈希

    哈希表 是一种使用哈希函数组织数据的数据结构,它支持快速插入和搜索。 哈希表(又称散列表)的原理为:借助 哈希函数,将键映射到存储桶地址。更确切地说, 1.首先开辟一定长度的,具有连续物理地址的桶数组; 2.当我们插入一个新的键时,哈希函数将决定该键应该

    2024年02月09日
    浏览(42)
  • 哈希表----数据结构

    如果你是一个队伍的队长,现在有 24 个队员,需要将他们分成 6 组,你会怎么分?其实有一种方法是让所有人排成一排,然后从队头开始报数,报的数字就是编号。当所有人都报完数后,这 24 人也被分为了 6 组,看下方。 (你可能会让 1~4 号为第一组,5~8 号为第二组……但

    2024年02月05日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包