【数据结构与算法】哈希专题

目录

[​​​​​​【题目链接】217. 存在重复元素 - 力扣(LeetCode)](#【题目链接】217. 存在重复元素 - 力扣(LeetCode))

[【题目链接】1. 两数之和 - 力扣(LeetCode)](#【题目链接】1. 两数之和 - 力扣(LeetCode))

[【题目链接】49. 字母异位词分组 - 力扣(LeetCode)](#【题目链接】49. 字母异位词分组 - 力扣(LeetCode))

[【题目链接】128. 最长连续序列 - 力扣(LeetCode)](#【题目链接】128. 最长连续序列 - 力扣(LeetCode))

[【题目链接】560. 和为 K 的子数组 - 力扣(LeetCode)](#【题目链接】560. 和为 K 的子数组 - 力扣(LeetCode))

(一)哈希基础

哈希种类

哈希表的特点

C++中哈希常见用法总结

(二)补充


​​​​​​【题目链接】217. 存在重复元素 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> set;
        for(int i=0;i<nums.size();i++){
            if(set.count(nums[i]))
                return true;
            set.insert(nums[i]);
        }
        return false;
    }
};

**解题思路:**利用哈希集合的无重复元素的性质,将数组依次存入哈希集合中,边存边查是否有重复(当前数组值是否已存入过哈希集合)。

**时间复杂度:**O(n) 每个元素只有一次检查+加入。

**空间复杂度:**O(n) 最坏情况需要存入所有元素才能得到结果。

【题目链接】1. 两数之和 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        for(int i=0;i<nums.size();i++){//枚举两个数中靠右侧的数a,维护哈希表存前面的[0,i-1]去找b
            int a=nums[i];
            int b=target-a;
            if(map.find(b)!=map.end())
                return {i,map.find(b)->second};
            map[a]=i;
        }
        return {};
    }
};

**解题思路:**利用哈希表的快速查找思想,假设b+a=target,此时只需要固定一个,去哈希表找另一个即可。遍历数组中的每个值numsi做一次a(两个数中靠右侧的数),维护一个哈希表来存储已遍历过的元素,对于每个数去哈希表中找是否有目标b。

**时间复杂度:**O(n) 需要遍历一遍所有元素才能得到结果,在哈希表中的都是O(1)。

**空间复杂度:**O(n) 哈希表的构建。

【题目链接】49. 字母异位词分组 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> map;
        for(auto i:strs){
            string key=i;
            sort(key.begin(),key.end());
            map[key].push_back(i);
        }
        vector<vector<string>> result;
        for(auto k :map){
            result.push_back(k.second);
        }
        return result;
    }
};

**解题思路:**利用排序+哈希表无重复元素和查找快的性质,维护pair<key, value>哈希表记录字母异位词(两个字符串排序之后相等)<模版, 此模版下排序前的字符串构成的数组>。

**时间复杂度:**O(nklogk),k是所有字符串中最长字符串的长度。 排序、遍历

**空间复杂度:**O(nk) 哈希表存储、排序(临时字符串哈希开销)

【题目链接】128. 最长连续序列 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> set_nums;
        for(auto i:nums){
            set_nums.insert(i);
        }
        int max=0;
        for(auto i:set_nums){
            if(!set_nums.count(i-1)){//当前数-1不存在的时候,它是起点才有可能最大
                int current=i;
                int flag=1;
                while(set_nums.count(current+1)){
                    current++;
                    flag++;
                }
                if(flag>max) max=flag;
            }
        }
        return max;
    }
};

**解题思路:**利用哈希集合查找快和无重复元素的优势,以及贪心思想,先将数组存入无重复元素的哈希集合,遍历集合中的每个元素i,看集合中是否存在i+1,...,i+k的值,并统计长度为k+1,过程中保存最长的序列长度。(考虑最优解不可能出现在"数-1"存在的数开头的序列上,可以优化时间,只枚举不存在"数-1"的数为起点)

时间复杂度: O(n) 因为每个元素恰好被访问一次。(for枚举所有数n次,while循环枚举所有非起点元素,而非起点元素最多也只有n个)

**空间复杂度:**O(n) 哈希表存储

【题目链接】560. 和为 K 的子数组 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        vector<int> sum_nums;
        int sum=0;
        for(auto i:nums){
            sum+=i;
            sum_nums.push_back(sum);
        }
        int res=0;
        unordered_map<double,int> map;
        map[0]=1;
        for(auto i:sum_nums){
            int target=i-k;
            if(map.count(target))
                res+=map[target];
            map[i]++;
        }
        return res;
    }
};

**解题思路:**利用前缀和+哈希两数之和的思想。问题是找到x1+x2+...+xm=k,子数组的和可以转化为两个前缀和之差,即Si-Sj=k是目标(类似前面两数之和的目标)。枚举靠右的Si,找是否出现过Si-k。维护键值对哈希表记录每个前缀和的出现次数res,即为n个子数组以位置i结尾和为k。

**时间复杂度:**O(n)

**空间复杂度:**O(n)

(一)哈希基础

哈希种类

1、哈希集合(set / unordered_set):只存单个元素,元素本身就是 "唯一标识",没有 first /second,底层红黑树有序,平均时间复杂度O(nlogn)

2、哈希表(map / unordered_map):存 key-value 键值对,元素是 pair,所以才有 k.first(键)、k.second(值),底层哈希映射哈希表无序,平均时间复杂度O(n)

哈希表的特点

哈希表是一种基于哈希函数(Hash Function)的数据结构,用于实现键值对的快速存储和查找。它的主要特点是:

  • 快速查找:平均情况下,哈希表的查找、插入和删除操作的时间复杂度都是 O(1)。
  • 键值对存储:哈希表存储的是键值对,每个键(Key)唯一对应一个值(Value)。
  • 哈希函数:通过哈希函数将键映射到哈希表中的位置,从而实现快速存取。

C++中哈希常见用法总结

哈希集合

#include<unordered_set>

unordered_set<int> set; //哈希集合

set.erase(numsi); //删除数字

set.insert(numsi); //插入数字

①set.count(numsi) ②set.find(b)!=set.end() //是否存在numsi,存在返回true

哈希表

#include<unordered_map>

unordered_map<int,int> map; //哈希表,类型任意

mapb=i; //b对应的值为i

map.erase(b); //删除key为b的对

①map.find(b)!=map.end() ②map.count(b) //目标是否存在

常见坑:

  • map.find(b)->second //key为b对应的val
  • mapb //若存在key是b,则返回val;不存在,插入新key=b,val=默认构造值,再返回val
  • //取出所有的second里的值到result
  • for(auto k :map)
  • result.push_back(k.second);//k.second取val,k.first取key

(二)补充

#include<bits/stdc++.h> //万能头文件

#include <iostream> //引入输入输出流头文件,C++ 标准输入输出库

cin>>; cout<<x<<endl;

using namespace std; //打开 std 标准命名空间(可以简写不写std::)

排序函数:

#include<algorithm>

sort(key.begin(),key.end());//默认从小到大排序

学习中,诚挚希望有心者指正和交流,经验或者方法都可。

相关推荐
ceclar1231 小时前
C#字节流与字符流
算法·c#·.net
LuminousCPP1 小时前
数据结构 - 单链表第二篇:单链表进阶操作
c语言·数据结构·笔记·链表
大白话_NOI1 小时前
【洛谷 P1024 】[NOIP2001 提高组] 一元三次方程求解 - 详细分析与C++实现
c++·算法
Matthew_zhu_1 小时前
P3374 【模板】树状数组 1 题解
算法
随意起个昵称1 小时前
区间dp-进阶题目1(进阶合并)
c++·算法·动态规划
伶俜661 小时前
鸿蒙原生应用实战(四)ArkUI 语音变声器:录音 + 4 种音效 + 音调变换算法
算法·华为·harmonyos
王老师青少年编程1 小时前
2022年CSP-X复赛真题及题解(T2:移动棋子)
c++·真题·csp·信奥赛·复赛·csp-x·移动棋子
玖玥拾1 小时前
C/C++ 数据结构(三)链表核心算法
c语言·数据结构·c++·链表
AKA__Zas2 小时前
芝士算法(滑动窗口片 2.0)
java·算法·leetcode·学习方法