代码随想录算法训练营第五天|Leetcode1. 两数之和,349. 两个数组的交集242.有效的字母异位词 202. 快乐数(内含Hash知识点详解)

1. 两数之和

建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。

建议大家先看视频讲解,然后尝试自己写代码,在看文章讲解,加深印象。

题目链接/文章讲解/视频讲解:代码随想录

 思路:242.有效的字母异位词

1,循环遍历数组,拿到每个数字x

2,以target-x作为key到hash表查找

(1)若没找到,将x作为key,将他的索引作为value放入hash表查找

(2)若找到了,返回x和他配对数的索引即可

我们直接来看代码

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

为什么用HashMap,因为它里面每一个元素可以存key和value,可以快速清晰地找到需要的元素,因为题目要求返回的是索引,我们将value作为键,索引作为值即可,这样我们调用get方法分时候返回的就是对应的索引。

349. 两个数组的交集

建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议大家忽略这个条件。 尝试去使用set。

题目链接/文章讲解/视频讲解:代码随想录

方法一:用Hashset

首先我们先来了解一下Hashset,他和HashMap的区别是什么呢

 

  1. 核心区别总结:

存储形式:

  • HashMap:存储键值对(key-value)
  • HashSet:仅存储单个元素

功能特点:

  • HashMap:

    • 可以根据键获取值
    • 可以存储重复的值(但键必须唯一)
    • 允许键和值为 null
  • HashSet:

    • 仅能存储元素值
    • 不允许重复元素
    • 只允许一个 null 元素

使用场景:

  • HashMap:

    • 需要存储键值对应关系时
    • 需要根据键快速查找值时
    • 需要统计频率或映射关系时
  • HashSet:

    • 需要去除重复元素时
    • 只关心元素是否存在时
    • 需要保证元素唯一性时

判断是否有重复元素,我们可以利用Hashset自动去重的功能,例如

并且hashset还有contains方法,也可以利于我们统计元素的出现数量。

这道题的思路是设置两个set,第一个set用来收集nums1中的元素,并将其添加进去,因为hashset自带的去重功能可以将重复元素去除,所以最后存放分hashset是没有重复元素的,然后我们再遍历nums2,如果set1里面的元素包含nums2中的元素,我们将这个元素加入set2,这个set2其实就是两个数组的交集,因为题目中要求返回int,所以我们再将set转化成int的类型即可,我们来看代码

import java.util.HashSet;
import java.util.Set;

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;
    }
}

方法二:用哈希数组 

为什么用哈希数组,什么是哈希数组

让我解释为什么这里使用哈希数组而不是普通数组:

哈希数组的特殊用途:
// 哈希数组用法
int[] hash1 = new int[1002];
for(int i : nums1) {
    hash1[i]++;  // 数组索引作为键,值作为计数器
}

// 普通数组用法
int[] normal = new int[nums1.length];
for(int i = 0; i < nums1.length; i++) {
    normal[i] = nums1[i];  // 索引只表示位置
}


// 使用哈希数组查找 - O(1)时间复杂度
if(hash1[i] > 0) {  // 直接通过索引判断数字i是否存在
    // 数字i存在
}

// 使用普通数组查找 - O(n)时间复杂度
boolean found = false;
for(int num : normal) {  // 需要遍历整个数组
    if(num == i) {
        found = true;
        break;
    }
}

 哈希数组更加适用于频繁查找和计数统计的操作,我们来看思路,我们创建两个hash数组,将两个输入数组中的值分别存入哈希数组中,这样数组的索引作为键,数组的值作为计数次数,然后我们得到了两个哈希数组,只要判断相同索引的两个哈希数组的值是否都大于0就行,如果都大于0,就说明这个元素是重复的,我们再创建一个动态数组吗,因为具备扩容的功能等,但是最后返回的时候还是需要创建一个普通数组,因为题目要求的返回方法是int,我们来看代码。

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int[] hash1 = new int[1002];
        int[] hash2 = new int[1002];
        for(int i : nums1)
            hash1[i]++;
        for(int i : nums2)
            hash2[i]++;
        List<Integer> resList = new ArrayList<>();
        for(int i = 0; i < 1002; i++)
            if(hash1[i] > 0 && hash2[i] > 0)
                resList.add(i);
        int index = 0;
        int res[] = new int[resList.size()];
        for(int i : resList)
            res[index++] = i;
        return res;
    }
}

242.有效的字母异位词

建议: 这道题目,大家可以感受到 数组 用来做哈希表 给我们带来的遍历之处。

题目链接/文章讲解/视频讲解: 代码随想录

 这道题的思路比较巧妙,我们结合代码来看

/**
 * 242. 有效的字母异位词 字典解法
 * 时间复杂度O(m+n) 空间复杂度O(1)
 */
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']++;     // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
        }

        for (int i = 0; i < t.length(); i++) {
            record[t.charAt(i) - 'a']--;
        }
        
        for (int count: record) {
            if (count != 0) {               // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        return true;                        // record数组所有元素都为零0,说明字符串s和t是字母异位词
    }
}

因为

// 在ASCII码中:
// 'a' = 97
// 'b' = 98
// 'c' = 99
// ...
// 'z' = 122

// 当我们做 charAt(i) - 'a' 时:
char c = 'c';  // ASCII值为99
char a = 'a';  // ASCII值为97
int index = c - a;  // 99 - 97 = 2
// 所以'c'对应的索引是2

将所有charat-a,我们可以将数组索引变成0——26,这样在建立数组的时候比较清晰,然后我们分别遍历两个数组,通过record记录,将遍历的每个字符记在对应的record索引上并将其值++,然后再遍历第二个数组时,对应的record--,这样子如果说,两个数组组成相同,最后应该所有record都是0.

202. 快乐数

建议:这道题目也是set的应用,其实和上一题差不多,就是 套在快乐数一个壳子

题目链接/文章讲解:代码随想录

 

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> record = new HashSet<>();
        while (n != 1 && !record.contains(n)) {
            record.add(n);
            n = getNextNumber(n);
        }
        return n == 1;
    }

    private int getNextNumber(int n) {
        int res = 0;
        while (n > 0) {
            int temp = n % 10;
            res += temp * temp;
            n = n / 10;
        }
        return res;
    }
}

思路和还是差不多,因为要判断快乐数,如果一个数陷入无限循环始终不为1,那么这个数就不是快乐数,我们可以设计一个hashset,当它不为1的时候,并且在之前没有出现过的时候(不然会陷入循环),Wimbledon将它添加在创建的hashset中,并且更新n为平方和,我们可以单独将计算的方法封装起来,每次%10,最后<0的时候退出循环,然后将每次%10的结果乘方相加,最后返回即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值