文档讲解:代码随想录(https://www.programmercarl.com/)
依然是补打卡! 追呀!😭
●242.有效的字母异位词
思路分析:
使用一个数组记录字符串S出现的次数,若该字母出现了一次,该位置的元素值加一;再遍历另外一个字符串,若出现一次就在数组对应位置的值减一。
因为字母是按顺序的,ASCII码上,每个字母也是相差1,随便取一个字母减去‘a’,就会得到该字母的下标了,然后在对应的record数组上,让该索引下的值+1
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public boolean isAnagram(String s, String t) {
//哈希法 用数组来实现
//定义一个统计字母出现次数的字符串
int[] record = new int[26];//开辟26个空间即可
for (int i = 0; i < s.length(); i++) {
record[s.charAt(i) - 'a']++;//因为字母是按顺序的,ASCII码上,每个字母也是相差1,随便取一个字母减去‘a’,就会得到该字母的下标了,然后在对应的record数组上,让该索引下的值+1
}
for (int i = 0; i < t.length(); i++) {
record[t.charAt(i) - 'a']--;
}
// 只要record数组里面,不是0的,说明该位置的元素在两个字符串中不相等
for (int count: record) {
if (count != 0) {
return false;
}
}
return true;
}
}
● 349. 两个数组的交集
一些提示:
- 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费!
- 这道题目没有限制数值的大小,就无法使用数组来做哈希表了
- 直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的; 简单的说就是,将一个数组转化成set集合,再将另外一个数组转化成set集合前判断是否在第一个集合出现过,出现过的再放到另外一个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];
}
//先将数组nums1存入set集合中,它当然也会去重,set集合中不会出现重复的元素
Set<Integer> set1 = new HashSet<>();
//存放结果
Set<Integer> resSet = new HashSet<>();
//遍历数组nums1
for (int i : nums1) {
set1.add(i);
}
//遍历数组nums2
for (int i : nums2) {
//判断nums1里面有没有
if (set1.contains(i)) {
resSet.add(i);
}
}
//将集合转化为数组
return resSet.stream().mapToInt(x -> x).toArray();
/*这个代码使用了Java 8中的流(Stream) API,它提供了一种高效的方式来处理数据。
具体来说,这句话的各个部分的作用如下:
resSet.stream():对 resSet 集合进行流操作,返回一个 Stream<Integer> 类型的流。
.mapToInt(x -> x):对流中的每个元素执行 map 操作,将元素映射成一个 int 类型的值。x -> x 表示的是一个Lambda表达式,它的作用是将元素原封不动地映射成 int 类型。
.toArray():将流中的所有 int 值存储在一个 int 数组中,然后将该数组作为结果返回。*/
● 202. 快乐数
思路分析:
1.判断sum是否重复出现就可以使用HashSet。
2. 还有一个难点就是求和的过程,如果对取数值各个位上的单数操作不熟悉的话,做这道题也会比较艰难。
3.检查数字是否在哈希集合中需要 O(1)的时间,而对于其他数据结构,则需要O*(*n) 的时间。选择正确的数据结构是解决这些问题的关键部分
class Solution {
public boolean isHappy(int n) {
// 创建一个set集合存放结果
Set<Integer> resSet = new HashSet<>();
while (n > 0) {
int res = getNumber(n);//获取到累加结果
if (res == 1) {
return true;
}
if(resSet.contains(res)) {
return false;//下一次过来的时候要判断set集合里是否包含了
} else {
resSet.add(res);//存到集合
}
n = res;
}
//如果set集合中包含了累加的res就直接返回false
return false;
}
//取单数
private int getNumber(int n) {
int res = 0;
while (n > 0) {
int d = n % 10;//先取余
res += d * d;
n = n / 10;//再取整
}
return res;
}
}
● 1. 两数之和
为什么用哈希法?
-
要查询一个元素是否出现过,或者一个元素是否出现在一个集合里面的时候要用哈希法
-
因为用暴力法的话,要做两次for循环,时间复杂度是O(n^2)
-
为什么用map实现?
- 本题我们不仅要知道元素有没有遍历过,还要取出这个元素对应的下标 需要用key value 结构,key存放元素,value存放下标,key存放元素的话,可以用map.containsKey()来判断是否有对应的值
- set的局限,只能存放key,不能记录元素对应的下标
- 数组的局限,其大小是受限制的,如果给定的元素很少,哈希值太大,会造成空间的浪费。
-
map用来干什么?key和value 存放什么?
map用来存放访问过的数组的元素,及其下标。要用target减去数组中当前遍历的元素,得到一个差值,然后去map中询问key(map.containsKey()方法)是否存在此差值,存在就返回该差值对应的下标
class Solution {
public int[] twoSum(int[] nums, int target) {
// 哈希法 map实现
// 定义一个数组,存放结果
int[] res = new int[2];
// 临界判断
if(nums == null || nums.length == 0) {
return res;
}
Map<Integer, Integer> map = new HashMap<>();
// 遍历数组
for(int i = 0; i < nums.length; i++) {
int temp = target - nums[i];
if (map.containsKey(temp)) {
res[0] = map.get(temp);// 拿到值对应的下标
res[1] = i; // 当前的下标
break; // 找到了
}
map.put(nums[i], i);
}
return res;
}
}