算法训练day2:哈希表

news2024/9/23 23:31:09

哈希表理论基础

哈希表是根据关键码的值而直接进行访问的数据结构。

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!

常见的三种哈希结构

当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

这里数组就没啥可说的了,我们来看一下set。

在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:

 std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。

std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。

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

那么再来看一下map ,在map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的,因为key的存储方式使用红黑树实现的。

其他语言例如:java里的HashMap ,TreeMap 都是一样的原理。可以灵活贯通。

虽然std::set、std::multiset 的底层实现是红黑树,不是哈希表,std::set、std::multiset 使用红黑树来索引和存储,不过给我们的使用方式,还是哈希法的使用方式,即key和value。所以使用这些数据结构来解决映射问题的方法,我们依然称之为哈希法。 map也是一样的道理。

这里在说一下,一些C++的经典书籍上 例如STL源码剖析,说到了hash_set hash_map,这个与unordered_set,unordered_map又有什么关系呢?

实际上功能都是一样一样的, 但是unordered_set在C++11的时候被引入标准库了,而hash_set并没有,所以建议还是使用unordered_set比较好,这就好比一个是官方认证的,hash_set,hash_map 是C++11标准之前民间高手自发造的轮子。

242:有效字母的异位词

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            record[s[i] - 97]++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 97]--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                return false;
            }
        }
        return true;
    }
};

 349:两个数组的交集

使用数组做哈希表

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        int hash[1005] = {0}; // 默认数值为0
        for (int num : nums1) { // nums1中出现的字母在hash数组中做记录
            hash[num] = 1;
        }
        for (int num : nums2) { // nums2中出现话,result记录
            if (hash[num] == 1) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

使用unordered_set

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

202:快乐数

class Solution {
public:
    int getSum(int n)
    {
        int sum = 0;
        while(n)
        {
           sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int>set;
        while(1)
        {
            int sum = getSum(n);
            if(sum == 1){return true;}
            if(set.find(sum) != set.end()){return false;}
            else{set.insert(sum);}
            n = sum;
        }
    }
};

1:两数之和

map方法

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int>map;
        for(int i = 0;i<nums.size();i++)
        {
             auto tmp = map.find(target - nums[i]);
             if(tmp != map.end()){return {tmp->second,i};}
             else
             {
                 map.insert(pair<int, int>(nums[i], i));
             }
        }
        return {};
    }
};

454:四数相加

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
       unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
        for (int a :nums1) {
            for (int b : nums2) {
                umap[a + b]++;
            }
        }
        int count = 0; 
        for (int c : nums3) {
            for (int d : nums4) {
                if (umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

15:三数之和(没搞明白,有哈希和双指针两种办法,下面是双指针法)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // 正确去重a方法
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) right--;
                else if (nums[i] + nums[left] + nums[right] < 0) left++;
                else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
    }
};

使用数组和set来做哈希法的局限。

  • 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  • set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。

map是一种<key, value>的结构,本题可以用key保存数值,用value在保存数值所在的下标。所以使用map最为合适。

C++提供如下三种map::(详情请看关于哈希表,你该了解这些! (opens new window))

  • std::map
  • std::multimap
  • std::unordered_map

std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层实现是红黑树。

同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解),1.两数之和 (opens new window)中并不需要key有序,选择std::unordered_map 效率更高!

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

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

相关文章

LeetCode:454. 四数相加 II —— 哈希表为什么叫哈希表~

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 hash是什么&#xff0c;哈希表为什么叫哈希表&#xff1f; 一、&#x1f331;454. 四数…

【hello Linux】进程概念(下)

目录 1. 通过系统调用创建进程—fork 1.1 通过fork创建进程&#xff1a; 1.2 如何不退出 vim 直接执行命令呢&#xff1f; 3. fork创建进程的本质 4. 父子进程的分流&#xff1a; 2. 进程状态 3. 信号 3.1 显示全部信号 3.1 停止进程 3.2 继续进程 3.3 杀死进程 后台进程 4. 僵…

在pycharm2020上部署配置AutoGPT4.0,保姆级教程

前期环境及软件准备&#xff1a; 1&#xff09;pycharm版本2020及以上 2&#xff09;python版本3.10及以上 3&#xff09;pip版本20及以上&#xff08;新一点的版本&#xff09; 4&#xff09;安装git&#xff0c;无版本要求 正式开工 具体配置步骤如下&#xff1a; 1.AUTOGP…

【UE 粒子系统】使用GPU渲染粒子

GPU Sprite 是虚幻引擎4中可用的粒子类型之一。这些粒子首先在CPU上生成&#xff0c;但之后完全由显卡处理和计算。这样做的好处是&#xff0c;由于GPU负责处理计算&#xff0c;因此可以同时处理成千上万的粒子&#xff0c;从而实现更密集和更细节化的粒子系统。 在上一篇博客&…

PPC380AE102 HIEE300885R0102现代自动化技术

PPC380AE102 HIEE300885R0102现代自动化技术 ​ 交流伺服电机驱动器示例 目前世界人口已经达到了78亿&#xff0c;并且还在不断增加&#xff0c;预计到2050年将达到100亿。日益增长的人口既有对衣服&#xff0c;食物等基本必需品的需求&#xff0c;对舒适、安全生活的追求也不断…

C++并发数据结构设计

关键词&#xff1a;原子操作&#xff0c;无锁设计 引入问题-> 为什么需要原子操作-> 原子操作实现以及原理-> c原子操作接口-> c基于原子操作的数据结构设计-> 原子操作 什么是原子操作 所谓原子操作,就是"不可中断的一个或一系列操作" 。 2…

列表和元组(上)——“Python”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是Python中的列表&#xff0c;下面&#xff0c;让我们进入列表的世界吧 列表是什么, 元组是什么 创建列表 访问下标 切片操作 遍历列表元素 列表是什么, 元组是什么 编程中, 经常需要使用变量, 来保存/表示数据. 如果…

Swift 注释和文档

今天&#xff0c;我知道我写是什么&#xff0c;上帝和我知道 明天&#xff0c;我知道这个代码什么意思&#xff0c; 后天&#xff0c;我知道这是我写的代码&#xff0c; 一周后&#xff0c;这TM谁写的代码&#xff0c;此时只有上帝才知道啥意思 论代码注释的重要性。 普通…

React Native 混合ios android开发 及常用框架

英文文档&#xff1a;Setting up the development environment React Native 中文文档&#xff1a;集成到现有原生应用 React Native 中文网 ios 在集成过程中&#xff0c;需要修改package.json 和 Podfile&#xff0c;按文档中的内容&#xff0c;如果pod install过不了的…

用Kotlin 一步步抄作业写一个Redux

前言 我抄的作业 完全理解 redux&#xff08;从零实现一个 redux&#xff09; Issue #22 brickspert/blog GitHub 一.架构设计模式 1. mvc>mvp>mvvm->mvi 2.Redux实现类Mvi Android Mvi 与Redux对比&#xff0c;思想一致&#xff0c;单向数据流&#xff0c;单…

最优控制 3:最优控制理论中的极小值原理与动态规划

最优控制 3&#xff1a;使用极小值原理求解最优控制问题 引言极小值原理 t f t_f tf​ 固定的情况 t f t_f tf​ 自由的情况 动态规划连续系统 HJB 方程的推导 引言 经典变分法是一种特别强大的工具&#xff0c;但是它要求控制量必须可导且无界&#xff0c;这在很多问题中都是…

pandas读取列数不同的CSV文件

使用pandas读取每行不同列的CSV文件 对于序列模型而言&#xff0c;每条数据的大小都不一定相等&#xff0c;但对于一般的神经网络要求输入大小相等。目前的一种方法是选取当前数据集中最大长度的数据作为基准数据大小&#xff0c;其余的数据末尾补零来规范整个数据集每条数据的…

计算机:理解操作系统:内存篇(下)

内存 1. 指针与引用2. 进程的内存模型3. 幻象大师---操作系统4. 总结 本篇是 关于计算机内存最后一篇文章 什么是内存 C/C内存模型 堆区与栈区的本质 Java、Python等内存模型 Java内存模型 Jave中的堆区与栈区是如何实现的 Python内存模型 指针与引用 进程的内存模型 幻想大师-…

css tooltip (web.dev)

目录 版权介绍tool-tip在上居中动画效果宽度边界 tool-tip::after范围锥形渐变-webkit-mask尖角怎么来的? 附录完整代码 版权 本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/130262213. 介绍 https://web.dev…

【周末闲谈】AI作图,你真的了解它吗?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️周末闲谈】 系列目录 ✨第一周 二进制VS三进制 ✨第二周 文心一言&#xff0c;模仿还是超越&#xff1f; ✨第二周 畅想AR 文章目录 系列目录前言AI绘画&#x1f916;&#x1f916;&#x1f916;工作…

[C++]:万字超详细讲解多态以及多态的实现原理(面试的必考的c++考点)

文章目录 前言一、多态的定义及实现1.多态的构成条件2.c11的override和final3.重载&#xff0c;重写&#xff0c;重定义的比较4.抽象类5.多态的原理6.多继承中的虚函数表7.动态绑定和静态绑定总结 前言 多态的概念&#xff1a; 多态的概念&#xff1a;通俗来说&#xff0c;就是…

【Linux】使用systemd设置开机自启动命令

目录 1 使用使用systemd实现开机自动运行命令1.1 新建一个.service文件1.2 编写.service文件1.2.1 [Unit]1.2.2 [Service]1.2.3 [Install] 1.3 启动服务并设置自启动 2 编写Systemd服务文件的要点2.1 Systemd服务文件的位置2.2 Systemd服务文件的格式2.3 Systemd服务文件的基本…

【基础】Kafka -- 基础架构及核心概念

Kafka -- 基础架构及核心概念 初识 KafkaKafka 基本架构Kafka 主题与分区主题与分区分区副本机制 Replica高水位 HW 生产者生产者客户端必要的参数配置消息的发送序列化分区器生产者拦截器 原理分析重要的生产者参数 消费者消费者与消费者组消费者客户端必要的参数配置订阅主题…

MySQL 按关键字进行截取

一、问题背景 取MySQL数据表中某个字段中的IP信息。 如&#xff1a;t_log 表中的 user_ip 字段值为 {“username”:“miracle”,“ip”:“110.230.128.186”}&#xff0c;取出IP信息 110.230.128.186。 建表和初始化SQL语句&#xff0c;如下&#xff1a; SET NAMES utf8mb4…

GORM操作mysql数据库

对象就是程序的数据结构&#xff0c;关系是数据库。就是将程序的数据结构与数据库表对应起来。 在GORM是Go语言的ORM框架&#xff0c;将go的数据结构转化为数据库表&#xff0c;例如将结构体转化为数据库表。 引入gorm框架 远程下载gorm框架 go get -u gorm.io/driver/mysq…