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

news2024/11/25 23:51:12

一、哈希表介绍

1、定义

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

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号箱子中。

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

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

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

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

二、面试常考的算法

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;
}

2、追踪遍历的完整路径

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

示例:input:给定一颗树

           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;
}

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;
    }

}

 

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不相交";
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179313.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5、Python中的变量和表达式:变量的定义、赋值和数据类型转换

文章目录 Python中的变量和表达式:变量的定义、赋值和数据类型转换变量的定义变量的赋值数据类型转换注意事项表达式总结Python中的变量和表达式:变量的定义、赋值和数据类型转换 Python是一种高级编程语言,以其简洁明了的语法和强大的功能而闻名。在Python编程中,变量和表…

docker安装达梦数据库镜像并初始化实例设置数据库大小写不敏感

全文参考文章Docker - 通过容器安装部署达梦数据库教程 以及docker安装达梦V8数据库 UTF-8及大小写敏感问题 一、安装部署达梦数据库 下载完成之后使用FileZilla上传到服务器上面 启动命令 创建dm8的容器并设置大小写不敏感运行 docker run -d -p 5236:5236 \ --restartalwa…

链式前向星模板

建稠密图可以用邻接矩阵&#xff0c;但稀疏图再用邻接矩阵就很浪费空间了&#xff0c;有可能会爆空间复杂度。 可以用邻接表来实现邻接表建图&#xff0c;两种方法&#xff1a;1.链表 2.链式前向行 只讲第二种&#xff0c;比较常用简洁 链式前向星模板 #define IOS ios::syn…

详解vue3的ref和reactive

目录 Vue3是目前前端领域最受欢迎的JavaScript框架之一&#xff0c;它的核心是响应式数据系统。因此&#xff0c;Vue3的ref和reactive成为了Vue3的核心特性之一。通过使用这两个特性&#xff0c;我们能够轻松地构建出高效、可维护的Vue3应用程序。在本文中&#xff0c;我们将详…

CoTTA:连续的测试时域自适应方法

文章信息 论文题目为《Continual Test-Time Domain Adaptation》&#xff0c;该文于2022年发表于Conference on Computer Vision and Pattern Recognition (CVPR)会议上。文章提出了一种持续的测试时域自适应方法&#xff08;CoTTA&#xff09;&#xff0c;旨在应对非稳态和不断…

本周三商店更新:多款套装下线,四款升级武器带异色皮肤返厂

本周三将迎来26.2版本更新与11商店大更新&#xff0c;版本更新可点击26.2版本更新公告进行查看&#xff0c;这里不一一赘述了&#xff0c;下面大概罗列一下商店更新&#xff0c;有皮肤下架&#xff0c;大家还能趁最后时间入手&#xff0c;最重要的是四款升级武器返厂咯。 危险玩…

shell脚本入门之【变量的定义】

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《shell》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;…

vue.js实现科室无限层选中和回显

一、效果展示&#xff1a; 展示可选层级 查看选中的值 二、实现&#xff1a; <el-form-item label"相关科室:" prop"orgId"><el-cascaderpopper-class"cascader-my":options"orgOptions":show-all-levels"false"…

文献管理软件Zotero之入门篇(1)

文章目录 0、前言1、下载安装1.1、下载安装主程序1.2、下载安装浏览器插件 2、给Zotero添加插件以实现更多快捷功能2.1、插件安装方式2.2、插件获取方式2.2.1、直接在官网插件也下载2.2.2、在Zotero中文社区插件页下载 3、总结 0、前言 一直以来想找一个文献管理帮手&#xff…

二进制代码反汇编逆向工具:IDA Pro(WinMac)v7.7 汉化版

IDA Pro是一款交互式的、可编程的、可扩展的、多处理器的、交叉Windows或Linux WinCE MacOS平台主机来分析程序。它被公认为最好的花钱可以买到的逆向工程利器&#xff0c;已经成为事实上的分析敌意代码的标准并让其自身迅速成为攻击研究领域的重要工具。 IDA Pro的特点主要包括…

海康Visionmaster-全局脚本:通过全局脚本获取通讯输 入的参数并赋值给全局变量

全局脚本根据外部通讯输入的数值赋值给全局变量&#xff0c;实现输入与全局变量之间的数值绑定。&#xff08;一般应用于定位、标定等需要外界物理值的场景)。 第一步&#xff0c;在 vm 通讯管理中设置好通讯设备&#xff0c;连接 第二步&#xff0c;根据通讯设备、接收的信息…

基于CMT2119A 无线发射芯片的物联网技术研究

随着物联网技术的快速发展&#xff0c;无线通信设备的需求也越来越大。CMT2119A无线发射芯片作为一种先进的芯片技术&#xff0c;被广泛应用于物联网中。本文通过对CMT2119A无线发射芯片的研究&#xff0c;探讨了其在物联网技术中的应用&#xff0c;分析了其特点和优势&#xf…

【Rust 日报】2023-11-05 xAI使用Rust

马斯克xAI正式使用Rust 在深度学习研究的前沿&#xff0c;必须像构建数据集和学习算法一样谨慎地构建可靠的基础设施。为了创建 Grok&#xff08;一个类ChatGPT的产品&#xff09;&#xff0c;xAI构建了一个基于 Kubernetes、Rust 和 JAX 的自定义训练和推理套件。 xAI认为&…

【Linux】拓展:进程管理常见的7大问题

目录 一、如何判断一个程序是单线程还是多线程 二、僵尸进程是什么&#xff0c;有什么危害&#xff0c;如何解决 三、如何找回删掉的文件 四、删除文件以后&#xff0c;空间不释放 五、遇到一个病毒&#xff08;如死循环病毒&#xff09;&#xff0c;解决思路 六、机器开机…

《研发效能(DevOps)工程师》课程简介(四)丨IDCF

由国家工业和信息化部教育与考试中心颁发的职业技术证书&#xff0c;也是国内首个研发效能&#xff08;DevOps&#xff09;职业技术认证&#xff0c;内涵1000页学习教材2000分钟的课程内容讲解460多个技术知识点300多道练习题。涵盖【组织与协作】、【产品设计与运营】、【开发…

数据库系统概念系列 - 数据库系统的历史

从商业计算机的出现开始&#xff0c;数据处理就一直推动着计算机的发展。事实上&#xff0c;数据处理自动化早于计算机的出现。Herman Hollerith 发明的穿孔卡片&#xff0c;早在20世纪初就用来记录美国的人口普查数据&#xff0c;并且用机械系统来处理这些卡片和列出结果。穿孔…

javascript中的new原理及实现

在js中&#xff0c;我们通过new运算符来创建一个对象&#xff0c;它是一个高频的操作。我们一般只是去用它&#xff0c;而很少关注它是如何实现的&#xff0c;它的工作机制是什么。 1 简介 本文介绍new的功能&#xff0c;用法&#xff0c;补充介绍了不加new也同样创建对象的方…

HDD最后的冲刺:大容量硬盘的奋力一搏

1.引言 在上一篇文章&#xff08;微软Azure云数据中心工作负载分享&#xff1a;SSD与HDD&#xff0c;何去何从&#xff1f;&#xff09;中&#xff0c;我们提到在应对SSD QLC/PLC大容量的挑战中&#xff0c;HDD也是在不断的努力&#xff0c;推出HAMR&#xff0c;SMR等新介质。…

【已解决】ModuleNotFoundError: No module named ‘dgl‘

禁止使用下面方法安装DGL,这种方法会更新你的pytorch版本&#xff0c;环境越变越乱 pip install dgl 二是进入DGL官网&#xff1a;Deep Graph Library (dgl.ai)&#xff0c;了解自己的配置情况&#xff0c;比如我cuda11.8&#xff0c;ubuntu&#xff0c;当然和linux是一样的 …

卡尔曼滤波之二:Python实现

卡尔曼滤波之二&#xff1a;Python实现 1.背景描述2.构建卡尔曼滤波公式2.1 预测2.2 更新 3.代码实现3.1 输入值3.2 pykalman包实现3.3 不使用Python包实现3.4 效果可视化 参考文献 了解了卡尔曼滤波之一&#xff1a;基本概念&#xff0c;可以结合代码来理解下卡尔曼滤波的2个预…