242.有效的字母异位词
1.解题思路
题目是为了查找两个字符串中字符出现的频率是否相同。
设置一个大小为26的数组,利用阿斯卡码值转换为数组位置实现,当多一个就++。
对另外一个,当出现一个,对应位置就--。
直到数组全部位置为0就是,否则不是。
2.我的实现
实现遇到的问题
字符转换成数组下标位置:
int n=x-'a'; array[n]++;
字符串的遍历:
for (char x:s.toCharArray() ) {//在这里x已经获得了s中的字符 int n=x-'a'; array[n]++; }
解释for-each:
int[] array = {1, 2, 3};
for (int num : array) {
System.out.println(num);
}
---->
for (int i = 0; i < array.length; i++) {
int num = array[i];
System.out.println(num);
}
即会把数组或字符串中的每一个遍历赋值给x
解释toCharArray():
String str = "Hello, World!";
char[] charArray = str.toCharArray();
--->
转换后的 charArray
内容如下:
['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!']
即toCharArray()
方法会将字符串中的每个字符复制到一个新的字符数组中,并返回这个数组,返
回值是一个数组。
代码
public boolean isAnagram(String s, String t) {
int[] array=new int[26];
for (char x:s.toCharArray()
) {//在这里x已经获得了s中的字符
int n=x-'a';
array[n]++;
}
for (char x:t.toCharArray()
) {
int n=x-'a';
array[n]--;
}
for (int i = 0; i < array.length; i++) {
if (array[i]!=0){
return false;
}
}
return true;
}
代码中的错误
无
3.标准实现
/**
* 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是字母异位词
}
}
区别
区别不大
charAt的解释:
public char charAt(int index)
返回值是一个char类型,返回字符串中下标为i的字符。
4.题目总结
当数据较少时,可以用数组类型做哈希表。
349. 两个数组的交集
1.解题思路
返回两个数组中相同的元素,元素不能重复,没有返回顺序要求。
将num1的所有数值放到哈希表里,再遍历num2,num2中元素是否出现在哈希表里,如果出现就放进result数组中。
2.我的实现
实现遇到的问题
这次遇到的问题比较多,第一次接触哈希表,所以查了很多资料,我放在set的主要功能及用法里面。
可以直接打印set:
Set
中元素的某些类型实现了合适的 toString()
方法
将set中的元素加到数组中:
int j=0; for (int x: hashSet2) { result[j++]=x; }
代码
public int[] intersection(int[] nums1, int[] nums2) {
//创建set
Set<Integer> hashSet=new HashSet<>();//用来接收nums1
Set<Integer> hashSet2=new HashSet<>();//用来接收result
//添加元素
for (int x:
nums1) {
hashSet.add(x);
}
//用另一个set接收交集
for (int i = 0; i < nums2.length; i++) {
if (hashSet.contains(nums2[i])){
hashSet2.add(nums2[i]);
}
}
//将set转化为数组
int[] result=new int[hashSet2.size()];//创建一个大小与接收set大小一样的数组
int j=0;
for (int x:
hashSet2) {
result[j++]=x;
}
return result;
}
代码中的错误:
没什么大错误
3.标准实现
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;
}
}
4.题目总结
数组
适合场景:
- 简单键值对存储:如果你需要存储简单的键值对,并且键是连续的整数(如从
0
开始),数组是一个简单而高效的选择。数组在访问和存储元素时非常高效,时间复杂度为O(1)
。
局限性:
- 数组不能处理非整数或非连续的键,不能动态调整大小(除非使用
ArrayList
),也不适合存储重复键。
set
适合场景:
- 存储唯一元素:当你需要存储一组唯一元素(没有重复),并且对元素的顺序没有要求时,
Set
是理想选择。Set
自动处理哈希冲突,且能快速插入、删除和查找元素,时间复杂度通常为O(1)
。
局限性:
Set
只适合存储唯一元素,不能存储键值对,因此无法直接查找与某个键关联的值。
map
适合场景:
- 键值对存储:当你需要存储键值对,并且键不是连续的整数时,
Map
是最佳选择。Map
允许你通过键来快速查找对应的值,通常时间复杂度为O(1)
。它非常适合需要根据键查找值的场景。
局限性:
Map
允许键重复(如果使用HashMap
,会覆盖原有值),可能会有性能开销(特别是涉及到哈希冲突时),且不保证键的顺序(如果使用HashMap
)。
总结
- 使用数组:当键是连续整数且需要快速访问时使用数组。
- 使用
Set
:当你需要存储唯一元素且不关心顺序时使用Set
。- 使用
Map
:当你需要存储键值对,且需要通过键快速查找对应的值时使用Map
。
202.快乐数
1.解题思路
题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!
这其实就是循环的结束条件。当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。
所以这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
2.我的实现
实现遇到的问题
取各个位上的数值:
public static int pfh(int n){ int sum=0; while (n>0){ int tmp=n%10; sum+=tmp*tmp; n=n/10; } return sum; }
代码
public boolean isHappy(int n) {
Set<Integer> set=new HashSet<>();//默认是空的
while (true){
if (pfh(n)==1){
return true;
}else {
set.add(n);
n=pfh(n);
if (set.contains(n)){
return false;
}
}
}
}
//创建一个计算每位数平方和的方法
public static int pfh(int n){
int sum=0;
while (n>0){
int tmp=n%10;
sum+=tmp*tmp;
n=n/10;
}
return sum;
}
代码中的错误
无
3.标准实现
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;
}
}
区别
while (n != 1 && !record.contains(n)) {
record.add(n);
n = getNextNumber(n);
}
return n == 1;
这里条件写得更简洁,当n==1或record中包含了n时就停止循环
4.题目总结
-
循环检测:使用
Set
来检测是否进入了循环。若发现一个平方和结果已经出现过,说明数n
会进入无限循环,不是快乐数。
1.两数之和
1.解题思路
当要判断这个元素是否出现在集合中时,要想到用哈希法
需要判断一个元素是否遍历过,我们每次把遍历过的元素n加到集合里,我们需要判断需要的元素(result-n)是否在集合里出现过。
使用map,以出现过的元素作为key,这个元素对应的下标作为value
2.我的实现
实现遇到的问题
在后续Map的主要功能和用法中
代码
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
if(nums == null || nums.length == 0){
return result;
}
//创建一个map储存nums中的元素和其下标
Map<Integer,Integer> map=new HashMap<>();
//因为不能重复用里面的元素,所以应该先判断了再收集
map.put(nums[0],0);
for (int i = 1; i < nums.length; i++) {
int tmp=target-nums[i];
if (map.containsKey(tmp)){
result[0]=map.get(tmp);//先找前面的再添加,防止两个相等
map.put(nums[i],i);//相等的key对应的value会被更新
result[1]=map.get(nums[i]);
return result;
}
map.put(nums[i],i);
}
return result;
}
代码中的错误
需要注意的在上述代码注释中
3.标准实现
//使用哈希表
public int[] twoSum(int[] nums, int target) {
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]; // 遍历当前元素,并在map中寻找是否有匹配的key
if(map.containsKey(temp)){
res[1] = i;
res[0] = map.get(temp);
break;
}
map.put(nums[i], i); // 如果没找到匹配对,就把访问过的元素和下标加入到map中
}
return res;
}
区别
其实在map是空的时候也检测不到temp,因为是先检测再增添,所以可以从i=0时开始遍历。
4.题目总结
这个题目主要考察了算法的优化思路,利用哈希表可以将原本 O(n^2)
的时间复杂度降低到 O(n)
。同时,注意题目中的唯一解、不能重复使用元素以及返回下标等要求是解决这个问题的关键。
Set
的主要功能及用法
1. 创建 Set
Set
是一个接口,因此你不能直接实例化它。通常使用其实现类来创建 Set
对象。
Set<String> hashSet = new HashSet<>();
Set<String> linkedHashSet = new LinkedHashSet<>();
Set<String> treeSet = new TreeSet<>();
HashSet
:不保证元素顺序,基于哈希表实现,最常用。LinkedHashSet
:保证元素的插入顺序,基于哈希表和链表实现。TreeSet
:保证元素的自然顺序(或者使用Comparator
进行排序),基于红黑树实现。
2. 添加元素
使用 add()
方法将元素添加到 Set
中。Set
不允许重复元素,如果尝试添加一个已经存在的元素,add()
方法会返回 false
。
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Orange");
boolean isAdded = set.add("Apple"); // 返回 false,因为 "Apple" 已经存在
3. 删除元素
使用 remove()
方法删除 Set
中的指定元素。
set.remove("Banana"); // 删除 "Banana"
4. 检查元素是否存在
使用 contains()
方法检查 Set
中是否存在某个元素。
boolean hasApple = set.contains("Apple"); // 返回 true
boolean hasGrape = set.contains("Grape"); // 返回 false
5. 获取 Set
的大小
使用 size()
方法获取 Set
中元素的数量。
int size = set.size(); // 返回 2,因为有 "Apple" 和 "Orange"
6. 遍历 Set
你可以使用 for-each
循环或迭代器来遍历 Set
中的元素。
// 使用 for-each 循环
for (String fruit : set) {
System.out.println(fruit);
}
// 使用迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
7. 清空 Set
使用 clear()
方法可以清空 Set
中的所有元素。
set.clear(); // 清空 Set
8. 判断 Set
是否为空
使用 isEmpty()
方法检查 Set
是否为空。
boolean isEmpty = set.isEmpty(); // 如果 Set 为空则返回 true
Map
的主要功能和用法
Map
是 Java 中的一种集合类型,用于存储键值对(key-value pairs)。在 Map
中,每个键(key)都是唯一的,并且每个键都映射到一个值(value)。Map
提供了一系列操作,方便开发者进行数据存储、查找、更新和删除操作。下面是 Map
的主要功能及其用法
1. 创建 Map
Map
是一个接口,不能直接实例化。常用的实现类有 HashMap
、LinkedHashMap
和 TreeMap
。
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
// HashMap 实例化
Map<String, Integer> hashMap = new HashMap<>();
// LinkedHashMap 实例化
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
// TreeMap 实例化
Map<String, Integer> treeMap = new TreeMap<>();
}
}
HashMap
:不保证顺序,基于哈希表实现,查找和插入的速度最快。LinkedHashMap
:按插入顺序排列键值对,基于哈希表和双向链表实现。TreeMap
:按键的自然顺序或自定义顺序排列,基于红黑树实现。
2. 添加和更新键值对
使用 put()
方法添加或更新键值对。如果键已经存在,put()
方法会更新其对应的值。
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10); // 添加键值对
map.put("Banana", 20);
map.put("Apple", 15); // 更新键 "Apple" 的值
3. 查找值
使用 get()
方法根据键查找对应的值。如果键不存在,返回 null
。
int value = map.get("Apple"); // 返回 15
Integer nonExistentValue = map.get("Orange"); // 返回 null
4. 检查键或值是否存在
containsKey(Object key)
:检查是否存在指定的键。containsValue(Object value)
:检查是否存在指定的值。
boolean hasApple = map.containsKey("Apple"); // 返回 true
boolean hasValue10 = map.containsValue(10); // 返回 false
5. 删除键值对
使用 remove()
方法根据键删除对应的键值对。如果键存在,返回其对应的值;否则,返回 null
。
int removedValue = map.remove("Banana"); // 返回 20 并移除键 "Banana"
Integer nonExistentRemove = map.remove("Orange"); // 返回 null
6. 获取 Map
的大小
使用 size()
方法获取 Map
中键值对的数量。
int size = map.size(); // 返回 1,因为有 1 对键值对
7. 遍历 Map
可以通过 keySet()
、values()
和 entrySet()
方法来遍历 Map
。
// 遍历键
for (String key : map.keySet()) {
System.out.println("Key: " + key);
}
// 遍历值
for (Integer value : map.values()) {
System.out.println("Value: " + value);
}
// 遍历键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
8. 清空 Map
使用 clear()
方法可以清空 Map
中的所有键值对。
map.clear(); // 清空 Map
9. 判断 Map
是否为空
使用 isEmpty()
方法检查 Map
是否为空。
boolean isEmpty = map.isEmpty(); // 如果 Map 为空,则返回 true
Map
的常见实现类及应用场景
-
HashMap
:适用于大多数情况下的键值对存储,尤其是当顺序不重要时。它提供最快的查找和插入性能。 -
LinkedHashMap
:适用于需要维护插入顺序或访问顺序的场景,常用于实现缓存(如 LRU 缓存)。 -
TreeMap
:适用于需要按键的自然顺序或自定义顺序排列键值对的场景,如按字母顺序排序的单词计数器。
总结
Map
是 Java 中非常强大的集合类型,用于高效存储和查找键值对。选择合适的 Map
实现类(HashMap
、LinkedHashMap
、TreeMap
)可以根据具体需求优化性能和功能。