哈希表理论基础
哈希表也称散列表也在我写过的数据结构那里有所说明
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set (集合)
- map(映射)
当我们想使用哈希法来解决问题时,选择的数据结构通常取决于具体的问题需求:
-
数组:适用于需要有序存储和快速随机访问的情况。但是,数组的大小通常是固定的,而且查找特定元素的时间复杂度依赖于数组是否排序。
-
Set(集合):如
HashSet
,适用于需要存储唯一元素且不需要维护任何特定顺序的情况。在Java中,除了HashSet
,还有TreeSet
(基于红黑树实现,可以保证元素排序)和LinkedHashSet
(基于哈希表和链表实现,可以保证元素的插入顺序)。 -
Map(映射):如
HashMap
,适用于需要通过键(key)来查找值(value)的情况。Map
提供了从唯一键到值的映射,非常适合用来建立索引或者关联不同类型的信息。
HashSet
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
两者区别
HashSet
与HashMap
的主要区别在于:
-
Key和Value的使用:
HashMap
存储键值对(key-value pairs),其中键(key)是唯一的,而值(value)可以重复。HashSet
只存储元素(可以看作是键,没有对应的值),并且所有元素都是唯一的。 -
数据结构:虽然两者都基于哈希表,但是
HashMap
存储的是键值对,因此每个条目都包含一个键和一个值。而HashSet
的每个条目只包含一个值,这个值同时也作为键使用。 -
接口实现:
HashMap
实现了Map
接口,而HashSet
实现了Set
卡哥提示
什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考
虑哈希法。 这句话很重要,大家在做哈希表题目都要思考这句话。
242.有效的字母异位词
思路
由于在ASCCII中,一个字母对应一个数值,建立一个哈希数组,在数组里存储第一个字符串中各个字母出现的次数,再用第二个字符串对应减掉数组的数值,若全为零,则说明两个字符串字母出现的频率相同。
在范围较小的时候用数组,若较大用set,需要key值对应用map
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash=new int[26];
for(int i=0;i<s.length();i++){
hash[s.charAt(i)-'a']++;//-'a':是利用字符串中字母与a的差值,因为这些差值是固定的,++是记录个数
}
for(int i=0;i<t.length();i++){
hash[t.charAt(i)-'a']--;
}
for (int count: hash) {
if (count != 0) // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return false;
}
return true;
}
}
语法补充--增强for循环
1 for (dataType variableName :collectionOrArray) {
2 // 循环体
3}
这里的dataType
应该是你期望从集合或数组中取出的元素类型,variableName
是你为每次迭代中取出的元素定义的变量名,而collectionOrArray
是你想要遍历的数组或集合。
当执行这段代码时,循环会按照以下步骤进行:
-
初始化:循环开始前,首先检查
collectionOrArray
是否为空。如果是空的,循环将不会执行任何迭代并直接结束。 -
获取下一个元素:在每次迭代开始时,循环会从
collectionOrArray
中获取下一个元素,并将其赋值给variableName
。 -
执行循环体:一旦元素被赋值,循环体内的代码就会被执行。你可以在循环体内对元素进行任何操作,比如打印、修改等。
-
检查终止条件:在执行完一次循环体后,循环会检查是否还有更多的元素可供迭代。如果没有更多元素,循环结束;如果有,回到步骤2继续下一次迭代。
349. 两个数组的交集
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;
}
}
202. 快乐数
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. 两数之和
思路
由于得到target的值组合会有多种,所以不能用简单的两层循环、
先遍历一遍数组,同时判断该数值是否之前出现过,还有与其相匹配的值是否遍历过,位置在哪
需要存储元素的值和下标,所以要用Map
那值和下标谁是key、谁是value
这时就要想清楚查找的是谁,在Java的HashMap类中,我们可以使用 get(key) 方法来获取 key 对应的 value,由题意可知应该是通过知道符合匹配条件的值,来找到对应下标,所以key是值,value是下标
class Solution {
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;
}
}