算法训练第六天 | 哈希part1


哈希day1

1. LeetCode242 有效的字母异位词

题目

在这里插入图片描述

思路

暴力解法
  1. 第一种解法,s开一个外循坏,每遍历一个字符,就进入t开的内循环,查找是否有相同的字符出现,若有,标记此字符,退出内循环;接着往s的下一个字符遍历。遍历结束后,再次对t开循环,挨个字符检查是否都变成了刚才的标记,如果有的没有标记,则s和t一定存在某些字符的数量的不一致。
  2. 第二种解法, 利用sort函数,如果s和t是同位字符串,那么经过排序后,得到的结果应该是一样的。

记录几个常用的函数:
获取数组长度:array.length;
获取字符串长度:string.length();
将字符串转成字符数组:char[] array = string.toCharArray();
取字符串的第n个字符:char x = string.charAt(n);
数组排序:Arrays.sort(array);

哈希法

哈希中三个方法:数组,set,map,哈希的宗旨是用空间复杂度换取时间复杂度。
涉及到查找元素的,量较少的,用数组,量较多的,用set;要用到key-value的,用map。

此题哈希法思路:

  1. 新开一个int类型的数组,26个char的长度,分别对应a到z 26个字母,用来存储每个字母在s和t字符串中出现的次数。int类型数组默认初始值都为0。
  2. 首先遍历s字符串,(当前字符-'a') 即为当前字符在新开的数组中对应的索引。字符出现一次,就在其对应的索引位置+1,记录出现次数。
  3. 遍历s后,再遍历t,同样找到每次遍历到的字符在新开的数组中对应的索引,字符出现一次,就在其对应的索引位置-1,如果s和t同一个字符的出现次数相等的话,最后遍历结束,结果应该是新开的数组的每个元素都为0。
  4. 最后,判断元素是否都为0。

代码

暴力——排序

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }
}

暴力——挨个定位

class Solution {
    public boolean isAnagram(String s, String t) {
        char[] s1 = s.toCharArray();
        char[] t1 = t.toCharArray();
        if (s1.length != t1.length){return false;}
        for (int i = 0; i < s1.length; i++){
            for (int j = 0; j < t1.length; j++){
                if (t1[j] == s1[i]){
                    t1[j] = 1;
                }
            }
        }
        for (int i = 0; i < t1.length; i++){
            if (t1[i] != 1){
                return false;
            }
        }
        return true;
    }
}

哈希法

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

2. LeetCode349 两个数组的交集

题目

在这里插入图片描述

思路

【知识点】哈希法的数组和set

数组:新定义一个数组,数组中每个元素的索引代表key,元素的值代表value,可以快速地保存元素出现的次数。
优点:相比HashSet,节省空间和时间。空间上,数组的长度是确定的;时间上,HashSet每次的hash操作都会消耗不少时间。
缺点:数组定义的前提是要知道数组的大小。

HashSet:Set<Integer> set = new HashSet<>();针对这个set,有add()方法,去重地添加元素,即如果元素已经存在于set中,则不重复加入。还有contains()方法,查询set中是否包含某元素。size()方法,获取set里元素的个数。底层代码会帮助你实现哈希映射。
优点:大小不需要可控。
确定:时间长,空间消耗多。

本题set思路

遍历nums1,定义set1,去重存放nums1中的数字。遍历nums2,每遍历一个元素,查询是否在nums1中存在,若存在,则说明是二者交集,存到新开的setFinal里。最后setFinal里的所有元素即为交集。
因为题目要返回整型数组,从set转为数组有两种方法。
1、return resSet.stream().mapToInt(x -> x).toArray();
2、遍历set,挨个从set里取整数出来放到新定义的int[]数组里。
这两种方法,2比1在速度上有明显的优势,怀疑可能是在set里将元素转为整型再将set转为array很耗时。

本题哈希数组思路

因为题目里又加了1 <= nums1.length, nums2.length <= 10000 <= nums1[i], nums2[i] <= 1000这两个条件,所以可以定义一个1001长度的数组,从index0到index1000可以代表从0到1000的共计1001个数字。索引对应的值代表这个数字是否出现过。
先遍历nums1,对于每个元素,都在数组的对应位置将值设为1。再遍历nums2,对于每个元素,查询数组上对应位置的值是否为1,若为1,则说明这个数是二者交集,存到新定义的HashSet中。最终HashSet里的所有数字就是我们想要的结果了。
同样地,set需要转为整型数组,和上面两种方法一样。

【知识点】解法耗时分析

四种解法中,耗时排名:数组2 < set2 < 数组1 < set1
可得结论:set比数组耗时,从set转为数组的两个方法中,1又比2耗时。且对最终执行时间影响最大的是转化方法中1比2耗的时长。

【知识点】遍历数组与set的快速操作

快速操作:

for(int i : array)  // 对于array中的每个整型数字i进行遍历,i代表元素值,不是索引!!!   
for(int i : set)    // 对于set中的每个整型数字i进行遍历,i代表元素值,不是索引!!! 

代码

哈希set:

import java.util.HashSet;
import java.util.Set;
// import java.util.*

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(x -> x).toArray();
        
        //方法2:另外申请一个数组存放setRes中的元素,最后返回数组
        int[] arr = new int[resSet.size()];
        int j = 0;
        for(int i : resSet){
            arr[j++] = i;
        }
        
        return arr;
    }
}

哈希数组:

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int[] array = new int[1001];
        Set<Integer> set = new HashSet<>();
        
        for (int i : nums1){
            array[i] = 1;
        }
        for (int i : nums2){
            if (array[i] == 1){
                set.add(i);
            }
        }
        //方法1:将结果集合转为数组
        return set.stream().mapToInt(x -> x).toArray(); /3ms
        
        //方法2:另外申请一个数组存放setRes中的元素,最后返回数组
        int[] newArray = new int[set.size()];
        int j = 0;
        for (int i : set){
            newArray[j++] = i;
        }
        return newArray; //1ms

    }
}

3. LeetCode202 快乐数

题目

在这里插入图片描述

思路

首先明确一个点,题目中已经暗示了,只有两种情况,一是无限循环但不等于1,二是无限循环等于1。至于为什么一定会无限循环,答案是因为int类型最大值为为‭‭2 147 483 647‬‬, 所以平方和最大的数是1 999 999 999,平方和为1 + 81*9 = 724。任何数的平方和都在1到724之间,724次循环之内一定有重复的。

第二点,求每次的平方和的时候最好单独定义一个函数来求。

代码

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

4. LeetCode01 两数之和

题目

在这里插入图片描述

思路

【知识点】HashMap

定义:Map<Integer,Integer> map = new HashMap<>();
此题中,key为数字值,value为对应的索引
获取map中是否含有key:map.containsKey()
根据key值得value值:map.get(key)
插入一对新的key-value值:map.put(key, value)

代码

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

暴力法,不用hashmap:

class Solution {
   public int[] twoSum(int[] nums, int target) {
        int[] add = new int[2];
        for (int i = 0; i < nums.length; i++){
            for (int j = i + 1; j < nums.length; j++){
                if (nums[i] + nums[j] == target){
                    add[0] = i;
                    add[1] = j;
                    return add;
                }
            }
        }
        return add;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值