【算法专场】哈希表

news2025/2/22 17:33:37

目录

前言

哈希表

1. 两数之和 - 力扣(LeetCode)

算法分析

算法代码

面试题 01.02. 判定是否互为字符重排

​编辑算法分析

算法代码

217. 存在重复元素

算法分析

算法代码

 219. 存在重复元素 II

算法分析

算法代码

解法二

算法代码

算法分析

算法代码

49. 字母异位词分组

         算法分析

算法代码


前言

当我们想要快速查找某个值是否存在,或者想要对数据进行去重的时候,我们有没有方法可以解决上面这些问题?我们可以用哈希表。

哈希表

哈希表(Hash Table)是一种通过哈希函数将键(key)映射到表中位置来访问记录的数据结构,它具有高效的数据查找、插入和删除能力

我们什么时候可以使用哈希表?

在快速查找、频繁统计、快速映射、去重的情景下都可以使用哈希表。

我们来通过算法题目来更好的了解哈希算法。

1. 两数之和 - 力扣(LeetCode)

算法分析

本道题要求我们从数组中找两个数,并且两数之和为target。这道题可以采用暴力枚举,时间复杂度为O(n^2),如果在大量数据的情况下,是会超时的,那么我们可以采用哈希算法来解决这道题。

我们可以在hash表中存储元素以及其下标的映射。在遍历数组的过程,每次判断(target-当前元素)是否在hash表中存在,如果不存在,那就将

当前元素以及其下标存到hash表中,反之,如果找到,那么就取出下标返回。

为什么不将所有元素都放进去hash表再进行判断?这样的话就无法解决元素重复的问题。假设目标值为2,那么现在有1,我们如果将1先放进了hash表,那么我们在遍历的时候,可以找到1,但实际上这两个1是同一个。而如果我们先判断hash表中是否存在(target-当前元素)的元素,则可以避免出现重复的情况。

算法代码

/**
 * 解决方案:找出数组中两个数之和等于目标值的数对索引
 * 方法:使用哈希表实现
 * 时间复杂度:O(n),其中n是数组的长度
 * 空间复杂度:O(n),需要额外的空间来存储哈希表
 * 
 * @param nums 输入的整数数组
 * @param target 目标值,寻找两数之和等于此值
 * @return 返回一个长度为2的整数数组,包含满足条件的两个数的索引;如果不存在这样的数对,则返回{-1, -1}
 */
public int[] twoSum(int[] nums, int target) {
    // 利用哈希表来存储已经遍历过的数字及其索引,以便快速检查目标值与当前值的差值是否已经出现过
    HashMap<Integer,Integer> map=new HashMap<>();
    for(int i=0;i<nums.length;i++){
        // 检查当前数字与目标值的差值是否已经在哈希表中
        if(map.containsKey(target-nums[i])){
            // 如果差值存在,则找到了满足条件的两个数,返回它们的索引
            return new int[]{map.get(target-nums[i]),i};
        }else{
            // 如果差值不存在,将当前数字及其索引放入哈希表中,以供后续查找使用
            map.put(nums[i],i);
        }
    }
    // 如果遍历完整个数组后仍未找到满足条件的数对,返回{-1, -1}
    return new int[]{-1,-1};
}

时间复杂度为O(n),空间复杂度为O(n).

面试题 01.02. 判定是否互为字符重排

算法分析

本道题其实就是要判断两个字符串是否是一样,那么我们就可以两个hash表,来存储每次字符出现的次数,如果两个hash表对应的值都相等,那就说明两个字符串是一样的。

算法代码

    /**
     * 检查两个字符串是否互为字符重排
     * 
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @return 如果两个字符串互为字符重排,则返回true;否则返回false
     */
    public boolean CheckPermutation(String s1, String s2) {
        // 如果两个字符串长度不同,则它们不可能互为字符重排
        if (s1.length() != s2.length()) {
            return false;
        }
        // 如果任一字符串为空,则它们不可能互为字符重排
        if (s1.length() == 0 || s2.length() == 0) {
            return false;
        }
        // 创建一个哈希表来存储字符出现的次数,ASCII码共有128个字符,额外留一个位置给可能的空字符
        int hash[] = new int[129];
        
        // 遍历第一个字符串,统计每个字符出现的次数
        for (int i = 0; i < s1.length(); i++) {
            char ch = s1.charAt(i);
            hash[ch]++;
        }
        
        // 遍历第二个字符串,检查每个字符是否在哈希表中出现过,并减少相应字符的计数
        for (int i = 0; i < s2.length(); i++) {
            char ch = s2.charAt(i);
            // 如果字符在哈希表中的计数为0,则说明两个字符串的字符组成不同
            if (hash[ch] == 0) {
                return false;
            }
            hash[ch]--;
        }
        
        // 如果所有字符都能一一对应,且没有多余的字符,则两个字符串互为字符重排
        return true;
    }

217. 存在重复元素

算法分析

这道题就是要求在数组中有没有一个元素是出现超过两次的,我们可以用哈希算法来解决这道题,利用HashSet,如果set里面没有存在当前元素,就将当前元素存到set中;反之,如果有,则返回true。

算法代码

  public boolean containsDuplicate(int[] nums) {
        HashSet<Integer> hashSet=new HashSet<>();
        for(int i=0;i<nums.length;i++){
            if(hashSet.contains(nums[i])){
                return true;
            }
            hashSet.add(nums[i]);
        }
        return false;
    }

 219. 存在重复元素 II

算法分析

跟上一道题类似,不过这里还需要将下标进行存储,需要利用HashMap,在遍历的过程中,如果当前元素在hashmap中已经存在,那么就判断这两个元素之间的距离,如果小于k,则返回true,否则将当前元素以及其下标存储到hashmap中。 

算法代码

/**
     * 检查数组中是否存在两个相同的数字,且它们的索引之差的绝对值最大为k
     * 这个方法通过使用HashMap来跟踪数字及其最后出现的索引来实现
     * 如果发现重复数字,并且它们的索引之差不超过k,则返回true
     * 如果不满足这些条件,则在遍历完整个数组后返回false
     * 
     * @param nums 输入的整数数组
     * @param k 索引之差的绝对值的最大允许值
     * @return 如果找到至少一对索引之差的绝对值不超过k的相同数字,则返回true;否则返回false
     */
    public boolean containsNearbyDuplicate2(int[] nums, int k) {
        // 使用HashMap来存储数字及其对应的索引
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            // 检查当前数字是否已经在HashMap中存在
            if(map.containsKey(nums[i])){
                // 如果当前数字已存在,并且当前索引与之前索引之差不超过k,则返回true
                if(i-map.get(nums[i])<=k){
                    return true;
                }
            }
            // 更新或添加当前数字及其索引到HashMap中
            map.put(nums[i],i);
        }
        // 如果没有找到满足条件的数字对,则返回false
        return false;
    }

时间复杂度为O(n),空间复杂度为O(n)

解法二

对于这道题,我们还可以通过滑动窗口的思想来解决,题目要求在数组中找两个相同的元素,并且他们下标的距离要小于等于k,那么我们就可以通过维护一个长度为k的窗口。如果当窗口小于k的时候,那么我们就将元素添加到窗口中,如果窗口中存在和当前元素相同的元素,那么就直接返回true,反之,则将元素添加到窗口中,当元素的个数等于窗口的长度的时候,那就将左边第一个元素进行移除。这里我们借助HashSet来判断窗口中是否有重复的元素

算法代码

  /**
     * 检查数组中是否存在重复的元素,且它们的索引之差的绝对值最大为k
     * 这个方法用于识别在一定索引范围内是否存在重复的数字
     *
     * @param nums 一个整数数组,其中可能包含重复元素
     * @param k 索引之差的绝对值的最大允许值
     * @return 如果找到至少一对索引之差的绝对值不超过k的重复元素,则返回true;否则返回false
     */
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        // 使用HashSet存储当前窗口内的元素,以便快速检查重复
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            // 当窗口大小超过k时,移除窗口最左侧的元素,以保持窗口大小不超过k
            if (i > k) {
                set.remove(nums[i - k - 1]);
            }
            // 尝试将当前元素添加到HashSet中如果添加失败,说明存在重复元素,且索引之差不超过k
            if (!set.add(nums[i])) {
                return true;
            }
        }
        // 遍历完成后,如果没有返回true,说明没有找到满足条件的重复元素
        return false;
    }

算法分析

这道题相对于前面两道题来说,要难上一些。但是以及依旧可以借鉴上面题目的思想,利用滑动窗口+有序集合

根据题意,假设任意位置为i,值为u。我们希望在下标范围为[max(0,i-k),i)的范围内找到值范围在[u-t,u+t]范围内的数。如果我们每次遍历到任意位置的时候,都往前检查k个元素,这样时间负责度会达到O(nk),会超时,所以我们需要对检索后面k个元素进行优化,在java中,有一个TreeSet数据结构,能够帮助我们在有序集合内快速找到值小于等于u或者大于等于u的值。

算法代码

/**
     * 检查数组中是否存在两个索引之差不超过indexDiff且值之差不超过valueDiff的元素
     * 
     * @param nums 包含整数的数组
     * @param indexDiff 索引之间的最大差值
     * @param valueDiff 值之间的最大差值
     * @return 如果找到满足条件的元素对,则返回true;否则返回false
     */
    public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) {
        int n = nums.length;
        TreeSet<Integer> set = new TreeSet<>();
        for(int i=0;i<n;i++){
            int u=nums[i];
            //如果set不为空,从set中找到大于等于u的最小值(最接近u的数)
            Integer ceil = set.ceiling(u);
            //如果set不为空,且小于等于u的最大值(最接近u的数)
            Integer floor = set.floor(u);
            //检查是否存在满足条件的元素
            if(ceil!=null&&ceil-u<=valueDiff) return true;
            if(floor!=null&&u-floor<=valueDiff) return true;
            //将当前元素加入set
            set.add(u);
            //如果当前索引大于等于indexDiff,则从set中移除滑动窗口最左侧的元素
            if(i>=indexDiff) set.remove(nums[i-indexDiff]);
        }
        //遍历后仍未找到满足条件的元素,返回false
        return false;
    }

49. 字母异位词分组

 算法分析

这道题要我们找字符串的字符个数都相同的字符串,并将这些字符串都放在同一个列表中,那么我们可以采用哈希算法,这里借助HashMap,键key为字符串,值为一个存储着字符串的列表。在遍历字符串数组的时候,我们每次将字符串进行排序,再判断hashmap是否存储当前字符串,如果存在,则将当前元素添加到列表中,反之,则创建一个列表,并将当前元素添加到列表中。最后再将map中的值存储放到ans中。

算法代码

/**
 * 对字符串数组进行遍历,将其中的字符串转换为字符数组并排序,然后转换回字符串
 * 使用HashMap来存储这些转换后的字符串作为键,以及它们在原始数组中的出现情况作为值
 * 最后,将HashMap中的所有值收集到一个二维列表中并返回
 * 
 * @param strs 字符串数组,包含待处理的字符串
 * @return 返回一个二维列表,其中包含按照字母异位词分组的字符串列表
 */
public List<List<String>> groupAnagrams(String[] strs) {
    // 初始化答案列表
    List<List<String>> ans=new ArrayList<>();
    // 初始化哈希图,用于存储排序后的字符串作为键,以及它们的出现情况作为值
    HashMap<String,List<String>> map=new HashMap<>();
    // 遍历输入的字符串数组
    for(int i=0;i<strs.length;i++){
        // 获取当前遍历到的字符串
        String s=strs[i];
        // 将字符串转换为字符数组
        char[] chars=s.toCharArray();
        // 对字符数组进行排序
        Arrays.sort(chars);
        // 将排序后的字符数组转换回字符串
        s=new String(chars);
        // 如果排序后的字符串已经在哈希图中存在
        if(map.containsKey(s)){
            // 将当前字符串添加到该键对应的列表中
            map.get(s).add(strs[i]);
        }else{
            // 否则,创建一个新的字符串列表
            List<String> list=new ArrayList<>();
            // 将当前字符串添加到列表中
            list.add(strs[i]);
            // 将排序后的字符串和对应的字符串列表作为键值对存入哈希图
            map.put(s,list);
        }
    }
    // 遍历哈希图的所有键
    for(String key:map.keySet()){
        // 将每个键对应的值(字符串列表)添加到答案列表中
        ans.add(map.get(key));
    }
    // 返回答案列表
    return ans;
}


以上就是哈希算法篇的所有内容啦~

若有不足,欢迎指正~

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

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

相关文章

【设计模式】【行为型模式】迭代器模式(Iterator)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

mac 意外退出移动硬盘后再次插入移动硬盘不显示怎么办

第一步&#xff1a;sudo ps aux | grep fsck 打开mac控制台输入如下指令&#xff0c;我们看到会出现两个进程&#xff0c;看进程是root的这个 sudo ps aux|grep fsck 第二步&#xff1a;杀死进程 在第一步基础上我们知道不显示u盘的进程是&#xff1a;62319&#xff0c;我们…

如何下载AndroidStudio的依赖的 jar,arr文件到本地

一、通过jitpack.io 下载依赖库 若需要下载 com.github.xxxxx:yy-zzz:0.0.2 的 jar则 https://jitpack.io/com/github/xxxxx/yy-zzz/0.0.2/ 下会列出如下build.logyy-zzz-0.0.2.jaryy-zzz-0.0.2.pomyy-zzz-0.0.2.pom.md5yy-zzz-0.0.2.pom.sha1jar 的下载路径为https://jitpack…

CEF132编译指南 MacOS 篇 - 构建 CEF (六)

1. 引言 经过前面一系列的精心准备&#xff0c;我们已经完成了所有必要的环境配置和源码获取工作。本篇作为 CEF132 编译指南系列的第六篇&#xff0c;将详细介绍如何在 macOS 系统上构建 CEF132。通过配置正确的编译命令和参数&#xff0c;我们将完成 CEF 的构建工作&#xf…

Python大数据可视化:基于python的电影天堂数据可视化_django+hive

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 电影数据 看板展示 我的信息 摘要 电影天堂数据可视化是…

LLM之循环神经网络(RNN)

在人工智能的领域中&#xff0c;神经网络是推动技术发展的核心力量。今天&#xff0c;让我们深入探讨循环神经网络&#xff08;RNN&#xff09; 一、神经网络基础 &#xff08;1&#xff09;什么是神经网络 神经网络&#xff0c;又称人工神经网络&#xff0c;其设计灵感源于人…

Java:204 基于springboot零食销售商城的设计与实现

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统主要分为管理员和用户、商家。 用户可以使用网站首页的登录注册界面进行在线登录注册&#xff0c;并且注册登录后方可使用系统的各种功能以及购物…

harmonyOS的文件的增、删、读、写相关操作(fs/content)

注意: 操作harmonyOS的文件只能对app沙箱内的文件进行操作 牵扯到两个支持点: fs和content这两个API; 具体的操作方法看下图: 创建文件 //js 引入 import fs from "ohos.files.fs" import featureAbility from "ohos.ability.featureAbility"; // 上下…

【golang】量化开发学习(一)

均值回归策略简介 均值回归&#xff08;Mean Reversion&#xff09;假设价格会围绕均值波动&#xff0c;当价格偏离均值一定程度后&#xff0c;会回归到均值。 基本逻辑&#xff1a; 计算一段时间内的移动均值&#xff08;如 20 天均线&#xff09;。当当前价格高于均值一定比…

4090单卡挑战DeepSeek r1 671b:尝试量化后的心得的分享

引言&#xff1a; 最近&#xff0c;DeepSeek-R1在完全开源的背景下&#xff0c;与OpenAI的O1推理模型展开了激烈竞争&#xff0c;引发了广泛关注。为了让更多本地用户能够运行DeepSeek&#xff0c;我们成功将R1 671B参数模型从720GB压缩至131GB&#xff0c;减少了80%&#xff…

MySQL数据库(八)☞ 我是不是锁神

目录 1 全局锁的应用 2 索引对行锁的影响 3 表锁&#xff08;显式&#xff09;--表级锁 4 元数据锁 MDL(隐式)--表级锁 5 意向锁(Intention)--IS锁 IX锁--表级锁&#xff08;隐式&#xff09; 6 记录锁-(Record)-S锁 X锁 -- 行级锁 7 如何理解select ... lock in share …

AI法理学与责任归属:技术演进下的法律重构与伦理挑战

文章目录 引言:智能时代的新型法律困境一、AI技术特性对传统法理的冲击1.1 算法黑箱与可解释性悖论1.2 动态学习系统的责任漂移1.3 多智能体协作的责任稀释二、AI法理学的核心争议点2.1 法律主体资格认定2.2 因果关系的技术解构2.3 过错标准的重新定义三、责任归属的实践案例分…

【NLP】循环神经网络RNN

目录 一、认识RNN 二、RNN模型分类 三、传统RNN模型 3.1 结构分析 3.2 Pytorch构建RNN模型 3.3 优缺点 一、认识RNN RNN(Recurrent Neural Network)&#xff0c;中文称作循环神经网络&#xff0c;一般以序列数据为输入&#xff0c;通过网络内部的结构设计有效捕捉序列之…

pnpm, eslint, vue-router4, element-plus, pinia

利用 pnpm 创建 vue3 项目 pnpm 包管理器 - 创建项目 Eslint 配置代码风格(Eslint用于规范纠错&#xff0c;prettier用于美观&#xff09; 在 设置 中配置保存时自动修复 提交前做代码检查 husky是一个 git hooks工具&#xff08;git的钩子工具&#xff0c;可以在特定实际执行特…

Vue的简单入门 一

声明&#xff1a;本版块根据B站学习&#xff0c;创建的是vue3项目&#xff0c;用的是vue2语法风格&#xff0c;仅供初学者学习。 目录 一、Vue项目的创建 1.已安装15.0或更高版本的Node.js 2.创建项目 二、 简单认识目录结构 三、模块语法中的指令 1.v-html 1.文本插值…

VMware Workstate 的 Ubuntu18 安装 vmware tools(不安装没法共享)

在共享主机路径后&#xff0c;可以在&#xff1a; /mnt/hgfs/下方找到共享的文件。但没有安装vmware tool时是没法共享的。 如何安装vmware tool&#xff0c;网上版本很多。这里记录一下&#xff1a; VMware Workstation 17 Pro&#xff0c;版本&#xff1a;17.6.0 虚拟机系统…

GC 基础入门

什么是GC&#xff08;Garbage Collection&#xff09;&#xff1f; 内存管理方式通常分为两种&#xff1a; 手动内存管理&#xff08;Manual Memory Management&#xff09;自动内存管理&#xff08;Garbage Collection, GC&#xff09; 手动内存管理 手动内存管理是指开发…

UE求职Demo开发日志#32 优化#1 交互逻辑实现接口、提取Bag和Warehouse的父类

1 定义并实现交互接口 接口定义&#xff1a; // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "UObject/Interface.h" #include "MyInterActInterface.generated.h…

如何在 Mac 上解决 Qt Creator 安装后应用程序无法找到的问题

在安装Qt时&#xff0c;遇到了一些问题&#xff0c;尤其是在Mac上安装Qt后&#xff0c;发现Qt Creator没有出现在应用程序中。通过一些搜索和操作&#xff0c;最终解决了问题。以下是详细的记录和解决方法。 1. 安装Qt后未显示Qt Creator 安装完成Qt后&#xff0c;启动应用程…

多线程基础面试题剖析

一、线程的创建方式有几种 创建线程的方式有两种&#xff0c;一种是继承Thread&#xff0c;一种是实现Runable 在这里推荐使用实现Runable接口&#xff0c;因为java是单继承的&#xff0c;一个类继承了Thread将无法继承其他的类&#xff0c;而java可以实现多个接口&#xff0…