第三章 哈希表

news2024/11/23 13:22:18

目录

  • 一、有效的字母异位词
    • 1.1 赎金信
    • 1.2 字符异位词分组
    • 1.3 找到字符串中所有字母异位词
  • 二、两个数组的交集
    • 2.1 两个数组的交集 II
  • 三、快乐数
  • 四、两数之和
  • 五、四数相加 II
  • 六、三数之和
  • 七、四数之和

哈希解决问题一般有三种数据结构供选择:

  • 数组
  • map(映射)
  • set(集合)
    在这里插入图片描述在这里插入图片描述

一、有效的字母异位词

Leetcode 242

数组就是一个简单的哈希表,使用数组比使用 map 或者 unordered_map 更省时间和空间,因为后者需要创建哈希函数以及维护哈希表 or 红黑树。

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[30] = {0};
        for (char c: s) 
            record[c - 'a'] ++ ;
        for (char c: t)
            record[c - 'a'] -- ;
        for (int x: record)
            if (x) return false;
        return true;
    }
};

1.1 赎金信

Leetcode 383

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[30] = {0};
        for (char c: magazine) 
            record[c - 'a'] ++ ;
        for (char c: ransomNote) {
            if (!record[c - 'a'])
                return false;
            record[c - 'a'] -- ;
        }     
        return true;
    }
};

1.2 字符异位词分组

Leetcode 49

核心思路:找一个中间状态,让相同字符异位词均能映射到上面。这里将每个字符按照其 ASCII 码的大小排序后,所有相同的字符处在一个组内部即可。

move() 的作用:本来需要将整个字符串 copy 过去,使用move之后只需要把字符串的首地址赋值过去,可以减少一次拷贝操作,提高效率。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> dict;
        for (auto &str: strs) {
            string key = str;
            sort(key.begin(), key.end());
            dict[key].push_back(move(str));
        }
        vector<vector<string>> res;
        for (auto it = dict.begin(); it != dict.end(); it ++ )
            res.push_back(move(it->second));
        return res;
    }
};

时间复杂度: N N N 表示字符串个数, L L L 表示字符串平均长度。对于每个字符串,哈希表和 v e c t o r vector vector 的插入操作时间复杂度为 O ( 1 ) O(1) O(1),排序时间时间复杂度为 L l o g ( L ) Llog(L) Llog(L),所以总共时间复杂度为 O ( N L l o g L ) O(NLlogL) O(NLlogL)

1.3 找到字符串中所有字母异位词

Leetcode 438

使用一个哈希表来维护字符,先将字符串 p 中所有字符添加进哈希表中,然后使用一个滑动窗口来遍历字符串 s,字符进入窗口相应元素数目减一,出窗口加一。当元素数目为零的时候,表示该元素匹配。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char, int> cnt;
        for (auto c: p) cnt[c] ++ ;
        int tot = cnt.size(); // 记录种类数目
        vector<int> res;
        for (int l = 0, r = 0, satisfy = 0; r < s.size(); r ++ ) { // satisfy指当前窗口内部满足要求的种类数
            if ( -- cnt[s[r]] == 0) satisfy ++ ;
            if (r - l + 1 > p.size()) {
                if (cnt[s[l]] == 0) satisfy -- ;
                cnt[s[l ++ ]] ++ ;
            }
            if (satisfy == tot) res.push_back(l);
        }
        return res;
    }
};

二、两个数组的交集

Leetcode 349

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int x: nums2) 
            if (nums_set.find(x) != nums_set.end())
                result_set.insert(x);
        return vector<int>(result_set.begin(), result_set.end());
    }
};

2.1 两个数组的交集 II

Leetcode 350

方法一:哈希表(适用于进阶问题二)

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size()) return intersect(nums2, nums1);
        unordered_map<int, int> cnt; // 节省空间,只保存较小长度的数组
        for (int x: nums1) cnt[x] ++ ;
        vector<int> res;
        for (int x: nums2) 
            if (cnt.count(x)) {
                res.push_back(x);
                cnt[x] -- ;
                if (!cnt[x]) cnt.erase(x);
            }
        return res;
    }
};
  • 时间复杂度: O ( m + n ) O(m + n) O(m+n)
  • 空间复杂度: O ( m i n ( m + n ) ) O(min(m +n)) O(min(m+n))

方法二:双指针(适用于进阶问题一)

先对数组排序,然后使用两个指针分别指向两个数组开始,如果两个指针指向元素不相等,将数值较小的指针右移。如果两个指针指向元素相同,则将该元素加入结果中,并同时右移两个指针。当至少有一个指针超出数组范围结束。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        sort(nums1.begin(), nums1.end());
        sort(nums2.begin(), nums2.end());
        vector<int> res;
        for (int i = 0, j = 0; i < nums1.size() && j < nums2.size();) {
            if (nums1[i] == nums2[j]) {
                res.push_back(nums1[i]);
                i ++ , j ++ ;
            } else if (nums1[i] < nums2[j]) i ++ ;
            else j ++ ;
        }
        return res;
    }
};
  • 时间复杂度: O ( m   l o g   m + n   l o g   n ) O(m\ log\ m + n\ log\ n) O(m log m+n log n)
  • 空间复杂度: O ( m i n ( m , n ) ) O(min(m,n)) O(min(m,n))

三、快乐数

Leetcode 202

提示:如果出现过的数字再重复出现就进入循环了

注意,最终结果会存在三种可能:

  • 最终得到 1 1 1
  • 最终进入循环
  • 值会越来越大,接近无穷大

对于第三种情况,考虑每一位数的最大数字的下一个数(即每一位数平方之和)是多少。

几位数最大数字下一个数
1981
299162
3999243
49999324
1399999999999991053

在三位数的情况下,其各位数的平方和不可能大于 243 243 243,要么被困在 243 243 243 以下的循环内,要么跌到 1 1 1。所以对于四位数或者四位以上的数字,其在每一步(各位数平方和)之后都会丢失一位,直到降到 3 3 3 位为止。所以可以发现第三种出现无穷大的情况使不可能的。

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> st;
        while (true) {
            int sum = getSum(n);
            if (sum == 1) return true;
            if (st.find(sum) != st.end()) return false;
            else st.insert(sum);
            n = sum;
        }
    }
};

四、两数之和

Leetcode 1

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

五、四数相加 II

Leetcode 454

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> mp; // 记录数组1与数组2元素 (和,出现次数)
        for (int a: nums1) 
            for (int b: nums2)
                mp[a + b] ++ ;
        int count = 0;
        for (int c: nums3)
            for (int d: nums4)
                if (mp.find(0 - (c + d)) != mp.end())
                    count += mp[0 - (c + d)];
        return count;
    }
};

六、三数之和

Leetcode 15

这个题与上一题的区别在于,这个的结果不能重复。

如果使用哈希表,这个题目不好判重,会非常麻烦,细节很多。所以这里使用双指针的思路。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i ++ ) {
            if (i && nums[i] == nums[i - 1]) continue;
            if (nums[i] > 0) break;
            for (int l = i + 1, r = nums.size() - 1; l < r; ) // 双指针
                if (l < r && nums[l] + nums[r] + nums[i] > 0) r -- ;
                else if (l < r && nums[l] + nums[r] + nums[i] < 0) l ++ ;
                else {
                    res.push_back({nums[i], nums[l], nums[r]});
                    while (l < r && nums[l] == nums[l + 1]) l ++ ;
                    while (l < r && nums[r] == nums[r - 1]) r -- ;
                    l ++ , r -- ;
                }
        }
        return res;
    }
};

七、四数之和

Leetcode 18

这个题目和上一个题目类似,结果依然使不能重复的,唯一区别在于多了一层 for 循环。

注意四个数相加可能会溢出

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        for (int i = 0; i < nums.size(); i ++ ) {
            if (i && nums[i - 1] == nums[i]) continue;
            if (nums[i] >= 0 && nums[i] > target) break;
            for (int j = i + 1; j < nums.size(); j ++ ) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                if (nums[i] + nums[j] >= 0 && nums[i] + nums[j] > target) break;
                for (int l = j + 1, r = nums.size() - 1; l < r; ) // 双指针
                    if ((long long) nums[l] + nums[r] + nums[i] + nums[j] > target) r -- ;
                    else if ((long long) nums[l] + nums[r] + nums[i] + nums[j] < target) l ++ ;
                    else {
                        res.push_back({nums[i], nums[j], nums[l], nums[r]});
                        
                        // 去重
                        while (l < r && nums[l] == nums[l + 1]) l ++ ;
                        while (l < r && nums[r] == nums[r - 1]) r -- ;

                        // 找到答案,同时更新
                        l ++ , r -- ;
                    }
            }
        }
        return res;
    }
};

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

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

相关文章

图解LeetCode——234. 回文链表

一、题目 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 二、示例 2.1> 示例 1&#xff1a; 【输入】head [1,2,2,1] 【输出】true 2.2> 示例 2&#xff1a; 【输…

[Android Studio Tool]在Android Studio项目中如何使用CSV文件

文章目录 在Android Studio项目中如何使用CSV文件1. 前情提要&#xff1a;CSV文件的准备2. CSV文件在安卓项目一般存放在什么位置&#xff1f;3. Android Studio处理CSV文件的常用插件&#xff1f;4. 怎么调整Android Studio中对GBK编码的CSV文件的处理&#xff1f;以下是一个代…

就业内推 | 国企招运维工程师,红帽认证、华为认证优先

01 厦门中盾安信科技有限公司 &#x1f537;招聘岗位&#xff1a;应用运维工程师&#xff08;中级&#xff09; &#x1f537;职责描述&#xff1a; 1、负责平台应用系统的安装、配置、日常巡检、维护、故障的处理&#xff1b; 2、负责平台服务相关应用的部署、配置、日常巡检…

Java面试知识点(全)- Java IO知识点详细

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 从数据传输方式理解IO流 从数据传输方式或者说是运输方式角度看&#xff0c;可以将 IO 类分为: 字节流, 字节流读取单个字节&#xff0c;字符流读取单…

mock模拟数据 ,UmiJS构建项目

Mock是一个用于模拟数据的JavaScript库&#xff0c;可以根据需要生成各种数据类型的模拟数据&#xff0c;可以配合json server使用 并且可以使用拦截ajax请求来模拟后端API接口。 JSON Server则是一个使用JSON文件来模拟RESTful API的工具 > 默认安装到运行环境&#xff1…

MYSQL索引、事务、存储引擎

一、索引 索引是一个排序的列表&#xff0c;包含字段的值和相应的行数据的物理地址 作用&#xff1a;加快表的查询速度&#xff0c;可以对字段排序 如何实现&#xff1a;没有索引的情况&#xff0c;要查询某行数据&#xff0c;需要先扫描全表来定位某行数据。有索引后会通过…

java ThreadLocal

private ThreadLocal threadLocal new ThreadLocal(); threadLocal.set(0); (int) threadLocal.get(); 上面三行代码分别是定义、赋值和取值。 介绍&#xff1a; 我们只需要实例化对象一次&#xff0c;并且也不需要知道它是被哪个线程实例化。虽然所有的线程都能访问到这个T…

【Vue】学习笔记-Vue CLI $nextTick 过渡与动画

$nextTick 这是一个生命周期钩子 this.$nextTick(回调函数) 在下一次DOM更新结束后执行其指定的回调 什么时候用&#xff1a;当数据改变后&#xff0c;要基于更新后的新DOM进行某些操作时&#xff0c;要在nextTick所指定的回调函数中执行。 使用$nextTick优化Todo-List src/co…

winform-SunnyUI控件解决大小位置变化

文章目录 前言问题种类使用SunnyUI解决控件DPI问题&#xff08;分辨率问题&#xff09;1.添加配置文件app.manifest2.将配置文件中dpiAware打开3.添加uiStyleManager1控件并将控件中DPIScale设置为true4.效果图 使用FlowLayOutPanel解决控件边距问题1.问题样式2.使用FlowLayOut…

Grafana系列-统一展示-12-RED Method Dashboard

系列文章 Grafana 系列文章 概述 目前关于监控指标, 主流的有 3 个方法(Method): RED : Rate&#xff08;访问速率&#xff09;, Errors&#xff08;错误&#xff09;, Duration&#xff08;响应时长&#xff09; - 由 tom_wilkie 引入USE : Utilization&#xff08;利用率…

TEMPUS FUGIT: 2

环境准备 靶机链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;5i9p 虚拟机网络链接模式&#xff1a;桥接模式 攻击机系统&#xff1a;kali linux 2022.03 信息收集 1.对靶机进行端口和服务的扫描 nmap -sV -p- -A 10.10.10.130 可以看到22端口是关闭的 2.用…

线段树-哈工大数据结构与算法作业

title: 线段树-哈工大作业 date: 2023-05-16 11:42:26 tags: 数据结构与算法 线段树 问题&#xff1a; 区间查询求和问题:给定一个含有n个整数序列的数组A&#xff0c;查询任意区间最大值与区间和的复杂度为O(n)&#xff0c;若进行m次查询&#xff0c;则总的复杂度为O(mn)。…

深析AutosarNM 网络管理

深析AutosarNM 网络管理 深析AutosarNM 网络管理1. AutosarNM 网络管理相关的专业术语2. 各种模式下的各种状态下&#xff0c;报文的收发情况汇总如下表&#xff1a;3. AutosarNM网络管理使用的时间参数&#xff1a;4. AutosarNM网络管理唤醒请求(Wake Up Request)5. ​​​​​…

MySQL 5.7数据库下载与安装教程

说明&#xff1a; 安装环境:Win10 64位 软件版本:MySQL 5.7.35 解压版 一.下载 点击下载地址&#xff1a; MySQL :: Download MySQL Community Server (Archived Versions) https://downloads.mysql.com/archives/community/ 选择合适的版本下载 绿色框框 是正式版&#xff0…

第02讲:SpringCloudStream

一、什么是SpringCloudStream SpringCloudStream是SpringCloud的一个子项目&#xff0c;他提供了一套更加通用的操作MQ的解决方案 Destination Binder&#xff08;目标绑定器&#xff09; &#xff1a;微服务与消息中间件通信的组件Destination Bindings&#xff08;目标绑定&…

2023爱分析・云原生 IDE 市场厂商评估报告-行云创新(CloudToGo)

1. 研究范围定义 企业数字化转型初期&#xff0c;通过资源池云化&#xff0c;解决了IDC时代运维、部署、扩容的难题&#xff0c;但传统应用单体架构厚重、烟囱式架构等带来的一系列应用层面的问题并没有得到有效解决&#xff0c;云对业务的价值主要还停留在资源供给的阶段…

Scaled dot-prodect Attention的原理和实现(附源码)

文章目录 背景什么是AttentionAttention权重的计算方法1. 多层感知机法2. Bilinear方法3. Dot Product4. Scaled Dot Product Scaled dot-prodect Attention的源码实现 背景 要了解深度学习中的Attention&#xff0c;就不得不先谈Encoder-Decoder框架&#xff08;sequence to s…

拍立淘API接口说明文档 按图搜索淘宝商品API 实时数据返回

开发背景&#xff1a; 随着电商行业的不断发展&#xff0c;人们的购物需求日益增多。在购买商品时&#xff0c;很多人会通过搜索引擎、社交媒体等手段来获取信息或灵感。但是&#xff0c;在这些渠道中找到想要的商品并不容易&#xff0c;因为其中可能会混杂着一些广告或无关内…

Android内存优化检测工具LeakCanary使用

一、什么是LeakCanary leakCanary是Square开源框架&#xff0c;是一个Android和Java的内存泄露检测库。如果检测到某个activity有内存泄露&#xff0c;LeakCanary就是自动地显示一个通知&#xff0c;所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇…

Java 并发队列详解

一&#xff0c;简介 1&#xff0c;并发队列两种实现 以ConcurrentLinkedQueue为代表的高性能非阻塞队列以BlockingQueue接口为代表的阻塞队列 2&#xff0c;阻塞队列与非阻塞队列的区别 当阻塞队列是空的时&#xff0c;从队列中获取元素的操作将会被阻塞&#xff0c;试图从…