哈希表Hash table
一般哈希表用来快速判断一个元素是否出现在集合里
通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
哈希表是一种通过对于增删改查性能较好的结构,以hashset添加过程举例:
底层结构:
哈希表(数组、链表、红黑树)
1.创建hashset集合,内部会存在一个长度16的数组
2.调用集合的添加方法,会拿着对象的hashcode方法计算应存入的索引位置(哈希值%数组长度)
详细解释:拿着对象的hashcode方法得到原始哈希值,再拿着原始哈希值向右移动16位,做一次哈希扰动,然后拿着扰动后的值与原始哈希值做异或操作,来进行这种二次哈希操作,以期减少一些哈希冲突,在将来计算索引位置时避免太多的重复索引,将列表打散
哈希碰撞有两种解决方法, 拉链法(JDK8之后是尾插法)和线性探测法
哈希法解决问题一般有一下三种数据结构:
数组
set(集合)
map(映射)
有效的字母异位词
题目描述
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
**注意:**若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
解题思路
定义一个存放26个小写字母的整数数组 int hash[26]
hash[s.charAt(i) - 'a']++
i=1,若索引1为字符’b’,则’b’-‘a’=1,则hash[1]++;hash索引1存放的是b出现的次数,最后比较数组 hash中的元素全0即可,时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)。
自己解题
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26];//定义一个存放26个小写字母的整数数组hash
for (int i = 0; i < s.length(); i++) {
hash[s.charAt(i) - 'a']++;
}
for (int i = 0; i < t.length(); i++) {
hash[t.charAt(i) - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (hash[i] != 0) {
return false;
}
}
return true;
}
}
s.charAt(i) 返回指定索引处char值
s.length() 返回字符串长度
参考解题
/**
* 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是字母异位词
}
}
补充
String类相关的方法
查询API(String类的构造方法、字符串的遍历、截取、替换、切割、StringBuilder)
两个数组的交集
题目描述
解题思路
自己解题
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<>();
for (int num : nums1)
set1.add(num);// 使用HashSet对数组去重
Set<Integer> set2 = new HashSet<>();
for (int num : nums2)
set2.add(num);
int[] output = new int[nums1.length];
int index = 0;
for (int num : set1) {
if (set2.contains(num)) {
output[index] = num;
index++;
}
}
System.out.println(index);
return Arrays.copyOf(output, index);// 复制指定的数组,截取或用 null 字符填充(如有必要),以使副本具有指定的长度
}
}
output[index++] = num;
//
output[index] = num;
index++;
参考解题
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];
}
HashSet<Integer> set1 = new HashSet<>();
HashSet<Integer> resSet = new HashSet<>();
// 遍历数组1
for (int i = 0; i < nums1.length; i++) {
set1.add(nums1[i]);
}
// 遍历数组2的过程中判断哈希表中是否存在该元素
for (int i : nums2) {
if (set1.contains(i)) {
resSet.add(i);
}
}
// 方法1:将结果集合转为数组
return resSet.stream().mapToInt(x -> x).toArray();//stream 流
}
}
补充
1.HashSet无序去重查询效率O(1),增删效率O(1),
2.Arrays工具类常用方法
3.HashSet常用方法
4.输入判断:
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
快乐数
题目描述
解题思路
代码随想录
当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,---->哈希法
由于循环次数未知,所以使用while循环,则循环终止条件为:
while (n != 1 && !record.contains(n))
1)当变为1,是快乐数,跳出循环。
2)是之前循环到过的数字(利用HashSet类的contains方法),跳出循环。
将下一次次循环的数字通过函数 getNextNumber解算
自己解题
class Solution {
public boolean isHappy(int n) {
Set<Integer> res = new HashSet<>();
/*
* 循环终止条件:
* 1)当变为1,是快乐数,跳出循环。
* 2)是之前循环到过的数字(利用HashSet类的contains方法),跳出循环
*/
while (n != 1 && !res.contains(n)) {
res.add(n);
n = getNextNumber(n);
}
return n == 1;
}
private int getNextNumber(int n) {
int res = 0;
while (n > 0) {
int temp = n % 10;// 数字拆分成每一位-->每次对10取余即可
res += temp * temp;
n = n / 10;
}
return res;
}
}
参考解题
补充
注意循环终止条件
两数之和
题目描述
解题思路
代码随想录
1.当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,---->哈希法
2.我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适
自己解题
Map<Integer, Integer> map = new HashMap<>();//建立一个HashMap,用于存放键、值
找元素是否出现,所以key存放元素。查的是下标,值value存放下标。
class Solution {
public int[] twoSum(int[] nums, int target) {
/*
* 哈希法可以快速从目标数组中找到对应的值
* map 是能在最快的时间内,判断这个key是否在map里出现过
* 遍历的时候用一个map存数组的值和下标,因为我们是要看值是否遍历过所以,key应该为值,key-value:需要的是下标,因此value为下标
* 而target - 当前遍历到的当前值nums[i] = cha差值,通过这个差值当作key来遍历map判断数组中是否遍历过该元素,从而得到目标的两个下标
*/
Map map = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; i++) {
int cha = target - nums[i];
if (map.containsKey(cha)) {
return new int[] { (Integer) map.get(cha), i };
}
map.put(nums[i], i);
}
return new int[2];//代码运行到这说明不存在,返回空整数数组
}
}
参考解题
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<>();//建立一个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;
}
补充
HashMap常用方法
map.containsKey(key)查找key是否存在map集合中
map.get(key)
map.put(key,value)添加键值对
ps:部分图片和代码来自代码随想录和Leetcode官网