[Java][算法 哈希]Day 01---LeetCode 热题 100---01~03

LeetCode 热题 100---01~03   ------->哈希

第一题  两数之和

思路

最直接的理解就是   找出两个数的和等于目标数   这两个数可以相同 但是不能是同一个数字(从数组上理解就是内存上不是同一位置)

解法一:暴力法

暴力解万物 按照需求  我们需要将数组的任意不同位置的数两两相加 再去判断是否等于目标数target 那么很显然 利用for循环的嵌套 第一层for循环从头遍历到尾 表示第一个数字 ;第二个数从第一个数的后一位置开始遍历到尾部,表示第二个数字

因为题目中明确说明了每种输入只会对应一个答案  所以找到之后就可以直接返回了

那么对应的代码就是

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

时间复杂度为O(N^2)

解法二:双指针法

上述时间复杂度之所以高 是因为我们查找第二个数采用的也是循环 就导致了循环的嵌套,如果想降低时间复杂度,那么就需要降低第二个数的查找时间

其实这个思路也比较简单 就是我们先将数组进行排序 保证从小到大/从大到小排序(这里我们排从小到大),那么 我们就可以最开始从数组最左侧A和最右侧B的两个数据开始相加得到sum  如果sum>target 那么说明我们需要讲两个加数变小,已知一个加数A已经是最小 那么只能让B往前走一位从而减小数据(当然 如果倒数第二个数据和最后一个数据等大  自然得出来的结果还是会大于target  但是不妨碍我们继续判断),反之  如果sum<target 那么就需要增大加数,只有加数A能够增大 所以就需要将加数A向右移动一位,依此类推,直到找到数据

class Solution {
    public int[] twoSum(int[] nums, int target) {
        ArrayList<Integer> list=new ArrayList<>();
        for (int num : nums) {
            list.add(num);
        }

        int[] dest = Arrays.copyOf(nums, nums.length);
        Arrays.sort(dest);
        int slow=0;int fast=nums.length-1;
        while(slow!=fast){
            int sum=dest[slow]+dest[fast];
            if(sum==target){
                int i = list.indexOf(dest[slow]);
                int j = list.lastIndexOf(dest[fast]);
                return new int[]{i,j};
            }else if(sum<target){
                slow++;
            }else if(sum>target){
                fast--;
            }
        }
        return new int[]{};
    }
}

可以看到  时间复杂度确实变小了  但是变化不多

解法三:哈希

这才是这个题目的出题本意  使用Hash来进行判断

同解法二,我们需要的是减少第二个数字的查询时间,我们可以将每个数存入Hash表中,然后通过target-A=B来得到B 然后判断在Hash表中是否存在B即可  因为Hash的缘故 第二个数据被查询的时间减少了

因为要找寻的是下表  我们利用Map数据结构 数据作为Key  下标作为Value  这样我们就可以通过key来找到下标

那么我们遍历一遍数组 作为第一个数A 然后通过containsKey(T   Key)方法来判断是否存在第二个数据B  如果存在 就直接通过get方法获得B的下标返回即可

如果不存在 就将该数放到Map中  之所以先判断后放入 是防止先放入之后  会出现自己和自己相加等于target的情况

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }
}

第二题  字母异位词分组

思路  

首先  字母异位词就是指  对一个单词重新排列顺序后 得到一个新的单词  在这个题目中  可以理解为  给你一个字符串数组  判断该数组中哪些元素是由同一些字母组成的 

例如示例一   eat  和 ate  和tea 三个元素都是由  a  e   t 组合而成 所以他们三个归为一个数组中

如此一来 我们只需要想办法  将各个方式组成的元素 用不重复方法标识出来即可 

最好的方法就是统计字母次数  

解法一:编码-分类

我们对每一个元素遍历  然后利用  每个单词-‘a’ 得到ASCII码差值  对应一个int[26] 数组arr中的每一个数据,简单理解就是a对应arr[0]  b对应arr[1]......以此类推  最后  由相同的字母组成的单词所得到的arr数据肯定相同  然后我们将arr转化为字符串String 作为标识的key  value采用List<String> 将同一类的单词都存入到这个Map<String,List<String>> map=new HashMap<>()中即可

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> endList=new ArrayList<>();//最终返回的List
        Map<String,List<String>> map=new HashMap<>();
// 遍历整个字符串数组进行操作
        for (String str : strs) {
            String code=getCode(str);  // 求出每个元素对应的code  作为标识key
// 判断该key是否存在 如果存在 就放到对应的List中 反之 如不存在 就创造一个新的key并new一个新List将该元素放入
            if(map.get(code)!=null){  
                map.get(code).add(str);
            }else{
                map.put(code,new ArrayList<>());
                map.get(code).add(str);
            }
        }
//最后遍历整个Map  将value取出来即可
        map.forEach((x,y)->endList.add(y));
        return endList;
    }

    public static String getCode(String str){
        char[] charArray = str.toCharArray();
        int [] arr=new int[26];
        for (char c : charArray) {
            arr[c-'a']++;
        }
        return Arrays.toString(arr);
    }
}

解法二:排序

如果两个单词是属于字母异位词,那么两者的字母组成肯定是相同的,如果字母组成是相同的,那么两者对内部单词进行同样的排序方式得到的结果也肯定是一样的,所以,我们需要对每个元素单词内部进行排序,然后将结果一样的放到一起即可

其实这种方法和上述的编码分类思想差不多,解法一是我们利用字母数量进行一个编码,我们解法二其实就是将排序后的结果作为标识编码来进行区分

class Solution {
    public static List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> endList=new ArrayList<>();
        Map<String,List<String>> map=new HashMap<String, List<String>>();
        for(String x:strs) {
        	char[] arr=x.toCharArray();
        	Arrays.sort(arr);
        	String end=new String(arr);
        	if(map.containsKey(end)) {
        		map.get(end).add(x);
        	}else {
        		map.put(end,new ArrayList<String>());
        		map.get(end).add(x);
        	}
        }
        map.forEach((x,y)->{endList.add(y);});
        return endList;
    }
}

第三题 最长连续序列

思路

判断有无连续的序列,简单的方式就是遍历一遍,然后遍历每个数的时候,判断下一个数字是否等于前一个数字加一,等于的计数器+1,反之则归零

需要注意的是,需要考虑空数组,数组中存在相同元素的情况

解法一: 自己写的

我们自己的写法就是按照上述思路的遍历想法解题

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length==0) return 0; //空数组直接返回

        LinkedHashSet<Integer> temp = new LinkedHashSet<>();
        ArrayList<Integer> list=new ArrayList<>();
//我们需要set来去重  但是因为set本身是无序的  为了方便后续的比较后一位是否等于前一位加一
//就需要该集合是有序的  所以我们采用LinkedHashSet这种结构
        for(int x=0;x<nums.length;x++){
            temp.add(nums[x]);
        }
//去重后用List存储  方便转数组
        for (Integer i : temp) {
            list.add(i);
        }
        Integer[] array = list.toArray(new Integer[0]);
        int[] arr=new int[array.length];
        for (int i = 0; i < array.length; i++) {
            arr[i]=array[i];
        }
        
        int max=0;
        int number=0;
        Arrays.sort(arr);
        for (int i = 1; i < arr.length; i++) {
//遍历数组 判断前一位和后一位是否连续  连续+1  反之归零
            if(arr[i]==arr[i-1]+1){
                number++;
            }else{
                if(number>max){
                    max=number;
                }
                number=0;
            }
        }
        if(number>max){
            max=number;
        }
        return max+1;
    }
}

可能会有疑问  为啥中间需要用List集合来转存一下 而不是直接Set集合temp转数组arr呢?其实也是可以的  对比两者 内存消耗和时间消耗其实差不多  temp直接转Array在某些特殊情况中会比List转Array是稍微多消耗一些资源的  所以哪怕第一段代码需要额外开销来转存到List中 但是单纯的开销空间来创造一个List和遍历集合消耗也不大

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length==0) return 0;
        LinkedHashSet<Integer> temp = new LinkedHashSet<>();
        for(int x=0;x<nums.length;x++){
            temp.add(nums[x]);
        }
        Integer[] array = temp.toArray(new Integer[0]);
        int[] arr=new int[array.length];
        for (int i = 0; i < array.length; i++) {
            arr[i]=array[i];
        }
        int max=0;
        int number=0;
        Arrays.sort(arr);
        for (int i = 1; i < arr.length; i++) {
            if(arr[i]==arr[i-1]+1){
                number++;
            }else{
                if(number>max){
                    max=number;
                }
                number=0;
            }
        }
        if(number>max){
            max=number;
        }
        return max+1;
    }
}

解法二:官方解法---Hash法

官方解法还是很巧妙的,我们采取遍历的方式来找,很容易重复遍历判断相同序列

由于我们要枚举的数 x 一定是在数组中不存在前驱数 x−1 的,不然按照上面的分析我们会从 x−1x-1x−1 开始尝试匹配,因此我们每次在哈希表中检查是否存在 x−1即能判断是否需要跳过了。

增加了判断跳过的逻辑之后,时间复杂度是多少呢?外层循环需要 O(n) 的时间复杂度,只有当一个数是连续序列的第一个数的情况下才会进入内层循环,然后在内层循环中匹配连续序列中的数,因此数组中的每个数只会进入内层循环一次。根据上述分析可知,总时间复杂度为 O(n)符合题目要求。

class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        int longestStreak = 0;

        for (int num : num_set) {
            if (!num_set.contains(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                while (num_set.contains(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }

                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring-security authentication persistence

翻译版本【spring-security 6.2.1】persistence Persisting Authentication 用户第一次请求受保护的资源时&#xff0c;系统会提示他们输入凭据。提示输入凭据的最常见方法之一是将用户重定向到登录页面。未经身份验证的用户请求受保护的资源的HTTP交换可能如下所示: 例1。未…

前端实现支付跳转以及回跳

// 支付地址 const baseURL http://pcapi-xiaotuxian-front-devtest.itheima.net/ const backURL http://127.0.0.1:5173/paycallback const redirectUrl encodeURIComponent(backURL) const payUrl ${baseURL}pay/aliPay?orderId${route.query.id}&redirect${redirec…

PyTorch 2.2 中文官方教程(一)

PyTorch 秘籍 PyTorch 秘籍 原文&#xff1a;pytorch.org/tutorials/recipes/recipes_index.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 秘籍是关于如何使用特定 PyTorch 功能的简短、可操作的示例&#xff0c;与我们的全长教程不同。 PyTorch 原型示例 原文…

Linux嵌入式开发+驱动开发-中断

swi汇编指令可以产生软中断&#xff0c;以下是硬件中断的产生到执行完毕的全过程&#xff1a; 在自己设计的芯片“CPU响应中断”程序的第四个步骤可以转向“中断向量控制器”&#xff0c;中断向量控制器中存储中断元服务地址即处理中断处理程序的地址&#xff0c;而不用使用0X1…

ONLYOFFICE文档8.0新功能浅探

ONLYOFFICE文档8.0新功能浅探 上个月末这个月初的几天&#xff0c;ONLYOFFICE版本更新了&#xff01;更新到了一个比较整的大的版本号&#xff0c;8.0版本&#xff0c;看来这个生产力工具的升级速度基本上能保持每年两个版本号的速度&#xff0c;还是很快的&#xff0c;一般来…

Java强训day15(选择题编程题)

选择题 自连接使用一张表编程题 题目1 import java.util.Scanner;public class Main { public static int res(int n) {StringBuffer s new StringBuffer();while(n!0) {s.append(n%2);n/2;}int sum 0;String ss s.reverse().toString();for(int i0;i<ss.length()…

秋招上岸大厂,分享一下经验

文章目录 秋招过程学习过程项目经验简历经验面试经验offer选择总结 秋招过程 今天是除夕&#xff0c;秋招已经正式结束了&#xff0c;等春节过完就到了春招的时间点了。 运气比较好&#xff0c;能在秋招的末尾进入一家大厂&#xff0c;拿到20k的sp offer。 从九月份十月份就开…

TCP 传输控制协议——详细

目录 1 TCP 1.1 TCP 最主要的特点 1.2 TCP 的连接 TCP 连接&#xff0c;IP 地址&#xff0c;套接字 1.3 可靠传输的工作原理 1.3.1 停止等待协议 &#xff08;1&#xff09;无差错情况 &#xff08;2&#xff09;出现差错 &#xff08;3&#xff09;确认丢失和确认迟到…

【RT-DETR改进涨点】更加聚焦的边界框损失Focaler-IoU、InnerFocalerIoU(二次创新)

一、本文介绍 本文给大家带来的改进机制是更加聚焦的边界框损失Focaler-IoU已经我进行二次创新的InnerFocalerIoU同时本文的内容支持现阶段的百分之九十以上的IoU,比如Focaler-IoU、Focaler-ShapeIoU、Inner-Focaler-ShapeIoU包含非常全的损失函数,边界框的损失函数只看这一…

RCE(命令执行)知识点总结最详细

description: 这里是CTF做题时常见的会遇见的RCE的漏洞知识点总结。 如果你觉得写得好并且想看更多web知识的话可以去gitbook.22kaka.fun去看&#xff0c;上面是我写的一本关于web学习的一个gitbook&#xff0c;当然如果你能去我的github为我的这个项目点亮星星我会感激不尽htt…

C#用Array类的Reverse方法反转数组中元素

目录 一、Array.Reverse 方法 1.重载 2.Reverse(Array, Int32, Int32) 3. Reverse(Array) 4.Reverse(T[]) 5. Reverse(T[], Int32, Int32) 二、实例 1.Array.Reverse 方法4种重载方法综合实例 2.Reverse(Array)方法的实例 一、Array.Reverse 方法 反转一维 Array 或部…

Android修改系统默认字体

文章目录 前言一、方案1、将定制的custom_fonts.xml配置文件编译到系统中2、将自定义的字体ttf文件编译到系统中3、在系统的编译mk中添加fonts.mk的引用4、修改系统代码,使得优先加载使用custom_fonts.xml前言 Android系统中的字体配置文件为/system/etc/fonts.xml 关于fonts…

JVM之GC垃圾回收

GC垃圾回收 如何判断对象可以回收 引用计数法 如果有对象引用计数加一&#xff0c;没有对象引用&#xff0c;计数减一&#xff0c;如果计数为零&#xff0c;则回收 但是如果存在循环引用&#xff0c;即A对象引用B对象&#xff0c;B对象引用A对象&#xff0c;会造成内存泄漏 可…

linux之wsl2安装远程桌面

0. 安装后的效果 1. wsl中打开terminal并安装库 sudo apt-get purge xrdp sudo apt install -y xrdp sudo apt install -y xfce4 sudo apt install -y xfce4-goodies 2.优化显示 sudo sed -i s/max_bpp32/#max_bpp32\nmax_bpp128/g /etc/xrdp/xrdp.ini sudo sed -i s/xserverbp…

听说有 Hugging Face 陪伴的春节,是这样的…

辞旧迎新春节到&#xff0c;家家户户好热闹。Hugging Face 中国团队成员祝各位社区成员们新春快乐&#xff0c;万事如意&#xff01; 过去的一年我们持续看到 AI 技术的腾飞和发展&#xff0c;以及诸多机构为开源 AI 作出巨大的贡献。非常感谢将模型、数据集和应用 Demo 发布在…

寒假作业:2024/2/8

作业1&#xff1a;现有文件test.c\test1.c\main.c,编写Makkefile Makefile代码&#xff1a; CCgcc EXEa.out OBJS$(patsubst %.c,%.o,$(wildcard *.c)) CFLAGS-c -oall:$(EXE)$(EXE):$(OBJS)$(CC) $^ -o $%.o:%.c$(CC) $(CFLAGS) $ $^.PHONY:cleanclean:rm $(OBJS) $(EXE)效果…

网络安全产品之认识准入控制系统

文章目录 一、什么是准入控制系统二、准入控制系统的主要功能1. 接入设备的身份认证2. 接入设备的安全性检查 三、准入控制系统的工作原理四、准入控制系统的特点五、准入控制系统的部署方式1. 网关模式2. 控制旁路模式 六、准入控制系统的应用场景七、企业如何利用准入控制系统…

CTF--Web安全--SQL注入之Post-Union注入

一、手动POST注入实现绕过 账号密码检测 我们利用sqli-labs/Less-11靶场来进行演示&#xff1a; 我们可以看到一个登录页面 打开Less-11的根目录&#xff0c;我们打开页面的源代码(PHP实现)。 用VS-code打开文件&#xff0c;找到验证登录信息的代码行。 此形式的代码存在POST…

【Linux】Linux开发工具(yum、gdb、git)详解

一、软件包管理器 yum 1、什么是软件包 在 Linux 下安装软件&#xff0c;通常的办法是下载到程序的源代码&#xff0c;并进行编译&#xff0c;得到可执行程序。但这样太麻烦了&#xff0c;于是有些人把一些常用的软件提前编译好&#xff0c;做成软件包&#xff08;可以理解成…

奋斗与诗意的三纲八目

人生得有一个基调、总的宗旨、指导思想、根据、根本。当人做出一个重大决定时&#xff0c;绝非偶然&#xff0c;一定是背后的宗旨在起作用。你每天起床的动力&#xff0c;是否能热情洋溢地做事&#xff0c;也是这个宗旨在起作用。念天地之悠悠独怆然而涕下&#xff0c;忧思难忘…