【算法训练营Day05】哈希表part1

news2025/6/7 15:39:37

文章目录

  • 哈希表理论基础
  • 有效的字母异位词
  • 两个数组的交集
  • 快乐数
  • 两数之和

哈希表理论基础

几个值得关注的知识点:

  • hash表用于快速的判断元素是否存在(空间换时间)
  • 其原理就是将数据通过散列函数映射到bucket中,如果发生hash碰撞,常见的处理方法有拉链法、线性探测法等等
  • 在Java中几种常见的与hash表相关的数据结构:
    • 数组:判断、计数情况可使用
    • HashSet:存单个元素
    • LinkedHashSet:在HashSet的基础上增添了顺序性
    • HashMap:存键值对,线程不安全
    • LinkedHashMap:在hashmap的基础上增加了添加元素的顺序性
    • Hashtable:线程安全,一般不怎么用,使用ConcurrentHashMap替代

有效的字母异位词

题目链接:242. 有效的字母异位词

解题逻辑:

  • 要满足两个单词异位,换句话说就是要两个单词组成的字母以及字母的数量要一样。
  • 我们可以遍历第一个字符串,将第一个字符串的所有字符加入到hashmap中,key使字母,value是字母的个数
  • 对第二个字符串做同样的操作
  • 比对两个hashmap
    • 长度首先要一样
    • 再比较每一项

代码如下:

class Solution {
    public boolean isAnagram(String s, String t) {
        char[] ss =  s.toCharArray();
        Map<Character,Integer> scount = new HashMap<>();
        for(char word : ss) {
            if(scount.containsKey(word)) {
                scount.put(word,scount.get(word) + 1);
            }else {
                scount.put(word,1);
            }
        }

        char[] tt =  t.toCharArray();
        Map<Character,Integer> tcount = new HashMap<>();
        for(char word : tt) {
            if(tcount.containsKey(word)) {
                tcount.put(word,tcount.get(word) + 1);
            }else {
                tcount.put(word,1);
            }
        }

        if(scount.size() != tcount.size()) return false;

        for(Map.Entry<Character, Integer> entry: scount.entrySet()) {
            Character key = entry.getKey();
            Integer value = entry.getValue();
            if(tcount.get(key) == null) return false;
            if(!tcount.get(key).equals(value)) return false;
        }

        return true;

    }
}

这种方法比暴力解法好一点点,但是也显得非常臃肿,原因就是实现hash表的数据结构并不是最优解。我们仔细观察这道题可以发现既然是英文单词,那么最多只有26个字母。那么我们可以将26个字母散列到长度为26的数组中,然后每个位置上对字母进行计数。

代码如下:

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] record = new int[26];
        for (int i = 0; i < s.length(); i++) record[s.charAt(i) - 'a']++;
        for (int i = 0; i < t.length(); i++) record[t.charAt(i) - 'a']--;
        for (int count: record) if (count != 0) return false;
        return true; 
    }    
}

这里我们可以思考一下将数组作为简单的hash表的注意点:

  • 核心:将数组的索引作为key
  • 使用场景
    • 能将要散列的属性与数组索引构成联系
    • key为密集分布的整数,并且范围小且已知(如果键稀疏则会造成大量的内存空间浪费,范围无法确定数组是不能动态扩容的)

两个数组的交集

题目链接:349. 两个数组的交集

最容易想到的就是将两个数组转化为set,然后直接取交集:

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set1 = Arrays.stream(nums1).boxed().collect(Collectors.toSet());
        Set<Integer> set2 = Arrays.stream(nums2).boxed().collect(Collectors.toSet());
        set1.retainAll(set2);
        return set1.stream().mapToInt(Integer::intValue).toArray();
    }
}

答案是这么写的:

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
            return new int[0];
        }
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> resSet = new HashSet<>();
        //遍历数组1
        for (int i : nums1) {
            set1.add(i);
        }
        //遍历数组2的过程中判断哈希表中是否存在该元素
        for (int i : nums2) {
            if (set1.contains(i)) {
                resSet.add(i);
            }
        }
      
        //方法1:将结果集合转为数组
        return resSet.stream().mapToInt(Integer::intValue).toArray();
    }
}

两种方法的时间复杂度是一样的都是O(n),答案相当于只是把retainAll方法的逻辑展开了。如果还想优化效率,还是得依靠数组,因为本题限制了数组数据的范围,所以才考虑到可以使用这种方法:

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int[] record1 = new int[1002];
        int[] record2 = new int[1002];
        for(int num : nums1) record1[num] += 1;
        for(int num : nums2) record2[num] += 1;
        List<Integer> resultList = new ArrayList<>();
        for(int i = 0;i < 1002;i++) if(record1[i] > 0 && record2[i] > 0) resultList.add(i);
        return resultList.stream().mapToInt(Integer::intValue).toArray();
    }
}

快乐数

题目链接:202. 快乐数

解题逻辑:

本题直接按照题目的步骤写代码即可,但是要弄明白,每一个数字在执行的过程中只可能有两种结果:

  • 无限循环,返回false
  • 满足快乐数要求,返回true

既然无限循环返回false,那么我们就可以将结果进行一个记录,有重复直接返回false

代码如下:

class Solution {
    public boolean isHappy(int n) {
        int sum = n;
        Set<Integer> record = new HashSet<>();
        while(sum != 1) {
            int num = sum;
            sum = 0;
            while(num >= 1){
                int temp = num % 10;
                sum += temp * temp;
                num /= 10;
            }
            if(record.contains(sum)) return false;
            record.add(sum);
        }
        return true
    }
}

两数之和

题目链接:1. 两数之和

首先暴力解法很容易想到,这种方法的时间复杂度是N方:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        for(int i = 0;i < nums.length;i++) {
            for(int j = i + 1;j < nums.length;j++) {
                if(nums[i] + nums[j] == target) return new int[]{i,j};
            }
        }
        return null;
    }
}

如果要继续优化,我们就要想办法使用单层循环解决问题,一开始我想到遍历数组,把每个元素塞到hashmap中,key是数组元素,value该元素在数组中的索引,然后再次遍历数组,在map中寻找target - 遍历元素。但是这样的话无法处理target是两个相同元素的加和,因为hashmap的key是不可能重复的。既然这样的话那么我们就只能一边遍历数组,一边添加到hashmap中。

解题逻辑如下:

  • 初始化一个hashmap用以存储已经存在的其中一个加数
  • 遍历数组,在hashmap中寻找target - 遍历元素是否存在
    • 如果存在,通过key查找value结合当前遍历索引返回答案
    • 如果不存在,把该值添加到hashmap中,表示这个加数存在,可供后续使用
class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> records = new HashMap<>();
        for(int i = 0;i < nums.length;i++) {
            int need = target - nums[i];
            if(records.get(need) == null) records.put(nums[i],i);
            else return new int[]{i,records.get(need)};
        }
        return null;
    }
}

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

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

相关文章

Kafka 如何保证顺序消费

在消息队列的应用场景中&#xff0c;保证消息的顺序消费对于一些业务至关重要&#xff0c;例如金融交易中的订单处理、电商系统的库存变更等。Kafka 作为高性能的分布式消息队列系统&#xff0c;通过巧妙的设计和配置&#xff0c;能够实现消息的顺序消费。接下来&#xff0c;我…

【算法题】算法一本通

每周更新至完结&#xff0c;建议关注收藏点赞。 目录 待整理文章已整理的文章方法论思想总结模版工具总结排序 数组与哈希表栈双指针&#xff08;滑动窗口、二分查找、链表&#xff09;树前缀树堆 优先队列&#xff08;区间/间隔问题、贪心 &#xff09;回溯图一维DP位操作数学…

Modbus转Ethernet IP赋能挤出吹塑机智能监控

在现代工业自动化领域&#xff0c;小疆智控Modbus转Ethernet IP网关GW-EIP-001与挤出吹塑机的应用越来越广泛。这篇文章将为您详细解读这两者的结合是如何提高生产效率&#xff0c;降低维护成本的。首先了解什么是Modbus和Ethernet IP。Modbus是一种串行通信协议&#xff0c;它…

什么是终端安全管理系统(终端安全管理软件2024科普)

在当今数字化迅速发展的时代&#xff0c;企业面临着越来越多的信息安全威胁。为了应对这些挑战&#xff0c;保障公司数据的安全性和完整性&#xff0c;终端安全管理系统&#xff08;Endpoint Security Management System&#xff09;应运而生。 本文将为您深入浅出地科普2024年…

【JVM】Java类加载机制

【JVM】Java类加载机制 什么是类加载&#xff1f; 在 Java 的世界里&#xff0c;每一个类或接口在经过编译后&#xff0c;都会生成对应的 .class 字节码文件。 所谓类加载机制&#xff0c;就是 JVM 将这些 .class 文件中的二进制数据加载到内存中&#xff0c;并对其进行校验…

《C++初阶之入门基础》【C++的前世今生】

【C的前世今生】目录 前言&#xff1a;---------------起源---------------一、历史背景二、横空出世---------------发展---------------三、标准立世C98&#xff1a;首个国际标准版本C03&#xff1a;小修订版本 四、现代进化C11&#xff1a;现代C的开端C14&#xff1a;对C11的…

Apache APISIX

目录 Apache APISIX是什么&#xff1f; Lua Lua 的主要特点&#xff1a; Lua 的常见应用&#xff1a; CVE-2020-13945(Apache APISIX默认API Token导致远程Lua代码执行) ​编辑Lua脚本解析 CVE-2021-45232(Apache APISIX Dashboard API权限绕过导致RCE) Apache …

如何在 git dev 中创建合并请求

先将 自己的代码 推到 自己的远程的 分支上 在 创建 合并请求 根据提示 将 自己的远程的 源码 合并到 对应的分支上 然后 创建 合并请求 等待 对应的 人 来 进行合并就行

基于nlohmann/json 实现 从C++对象转换成JSON数据格式

C对象的JSON序列化与反序列化 基于JsonCpp库实现C对象序列化与反序列化 JSON 介绍 JSON作为一种轻量级的数据交换格式&#xff0c;在Web服务和应用程序中广泛使用。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读…

《T/CI 404-2024 医疗大数据智能采集及管理技术规范》全面解读与实施分析

规范背景与详细信息 《T/CI 404-2024 医疗大数据智能采集及管理技术规范》是由中国国际科技促进会联合河南科技大学、河南科技大学第一附属医院、深圳市人民医院等十余家医疗机构与企业共同制定的团体标准,于2024年5月正式发布实施。该规范是我国医疗大数据领域的重要技术标准…

国产三维CAD皇冠CAD在「金属压力容器制造」建模教程:蒸汽锅炉

面对蒸汽锅炉设计中复杂的曲面封头、密集的管板开孔、多变的支撑结构以及严格的强度与安全规范&#xff08;如GB150、ASME等&#xff09;&#xff0c;传统二维设计手段往往捉襟见肘&#xff0c;易出错、效率低、协同难。国产三维CAD皇冠CAD&#xff08;CrownCAD&#xff09;凭借…

C++中单例模式详解

在C中&#xff0c;单例模式 (Singleton Pattern) 确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。这在需要一个全局对象来协调整个系统行为的场景中非常有用。 为什么要有单例模式&#xff1f; 在许多项目中&#xff0c;某些类从逻辑上讲只需要一个实…

舆情监控系统爬虫技术解析

之前我已经详细解释过爬虫在系统中的角色和技术要点&#xff0c;这次需要更聚焦“如何实现”这个动作。 我注意到上次回复偏重架构设计&#xff0c;这次应该拆解为更具体的操作步骤&#xff1a;从目标定义到数据落地的完整流水线。尤其要强调动态调度这个容易被忽视的环节——…

Vue3中Ant-design-vue的使用-附完整代码

前言 首先介绍一下什么是Ant-design-vue Ant Design Vue 是基于 Vue 3 的企业级 UI 组件库&#xff08;同时兼容 Vue 2&#xff09;&#xff0c;是蚂蚁金服开源项目 Ant Design 的 Vue 实现版本。它遵循 Ant Design 的设计规范&#xff0c;提供丰富的组件和高质量的设计体系&…

Redis Sorted Set 深度解析:从原理到实战应用

Redis Sorted Set 深度解析&#xff1a;从原理到实战应用 在 Redis 丰富的数据结构家族中&#xff0c;Sorted Set&#xff08;有序集合&#xff09;凭借独特的设计和强大的功能&#xff0c;成为处理有序数据场景的得力工具。无论是构建实时排行榜&#xff0c;还是实现基于时间的…

若依框架修改模板,添加通过excel导入数据功能

版本&#xff1a;我后端使用的是RuoYi-Vue-fast版本&#xff0c;前端是RuoYi-Vue3 需求: 我需要每个侧边栏功能都需要具有导入excel功能&#xff0c;但是若依只有用户才具备&#xff0c;我需要代码生成的每个功能都拥有导入功能。​ 每次生成一个一个改实在是太麻烦了。索性…

web全栈开发学习-01html基础

背景 最近在付费网站学习web全栈开发&#xff0c;记录一下阶段性学习。今天刚好学完html基础&#xff0c;跟着教程画了个基础的网站。 样品展示: 开发工具 vscode Visual Studio Code - Code Editing. Redefined 常用插件 Prettier&#xff1a;格式优化 Live Sever:实时调…

多线程环境中,如果多个线程同时尝试向同一个TCP客户端发送数据,添加同步机制

原代码 public async Task SendToClientAsync(TcpClient targetClient, byte[] data, int offset, int length) {try{// 1. 检查客户端是否有效if (targetClient null || !targetClient.Connected){Console.WriteLine("Cannot send: client is not connected");ret…

【含文档+PPT+源码】基于微信小程序的旅游论坛系统的设计与实现

项目介绍 本课程演示的是一款基于微信小程序的旅游论坛系统的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 …

贝叶斯优化+LSTM+时序预测=Nature子刊!

贝叶斯优化与LSTM的融合在时间序列预测领域取得了显著成效&#xff0c;特别是在处理那些涉及众多超参数调整的复杂问题时。 1.这种结合不仅极大提高了预测的精确度&#xff0c;还优化了模型训练流程&#xff0c;提升了效率和成本效益。超参数优化的新篇章&#xff1a;LSTM因其…