哈希表理论基础
哈希表
哈希表是根据关键码的值而直接进行访问的数据结构。
数组就是一张哈希表,哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图所示:
哈希表一般用来快速判断一个元素是否出现集合里。
哈希函数
哈希函数通过特定编码方式,可以将其他数据格式转化为不同的数值,映射到哈希表上的索引数字。
如果hashCode得到的数值大于哈希表的大小,此时为了保证映射出来的索引数值都落在哈希表上,对数值做一个取模的操作。
哈希碰撞
如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。
哈希碰撞有两种解决方法, 拉链法和线性探测法。
拉链法
将发生冲突的元素都存储在链表中,这样就可以通过索引找到冲突元素。
拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
线性探测法
使用线性探测法,一定要保证tableSize大于dataSize,依靠哈希表中的空位来解决碰撞问题。
冲突的位置放了第一个元素,那么就向下找一个空位放置另一个元素的信息。所以要求tableSize一定要大于dataSize ,不然哈希表上没有空置的位置来存放冲突的数据了。如图所示:
常见哈希结构
使用哈希法来解决问题的时候,一般选择如下三种数据结构:
- 数组
- set (集合)
- map(映射)
242.有效的字母异位词
题目
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
**注意:**若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
示例1:
输入: s = "anagram", t = "nagaram"
输出: true
示例2:
输入: s = "rat", t = "car"
输出: false
提示:
- 1 <= s.length, t.length <= 5 * 104
s
和t
仅包含小写字母
思路
数组其实就是一个简单的哈希表,本题中字符串只有小写字符,可以定义一个数组,来记录字符串s、t里字符出现的次数。
s中每出现一次某字母,数组中相应位置++,t中每出现一次某字母,数组中相应位置–:
遍历字符串的时候,可以通过字母和 ‘a’ 的相对ASCII码获取相应下标。
字符串遍历完之后再遍历一次数组,检查数组中各元素是否为0。
题解
独立题解:
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26];
//使用.chatAt获取相应下标的字符
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 j = 0; j < 26; j++) {
if (hash[j] != 0)
return false;
}
return true;
}
}
349.两个数组的交集
题目
给定两个数组 nums1
和 nums2
,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
思路
HashSet是没有重复元素且无序的集合。
由题要求输出去重且不考虑顺序,可使用HashSet:
另外,题目给出提示:数组长度为1到1000,元素值为0到1000,所以也可以使用数组作为哈希表。
题解
独立题解:
使用数组+HashSet解决
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
int[] hash = new int[1001];
Set<Integer> hashset = new HashSet<Integer>();
for (int i : nums1) {
hash[i]++;
}
for (int i : nums2) {
if (hash[i] > 0) {
hashset.add(i);
}
}
int[] res = new int[hashset.size()];
int index = 0;
for (int i : hashset) {
res[index++] = i;
}
return res;
}
}
参考题解:
使用哈希数组解决
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;
}
}
202.快乐数
题目
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例1:
输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
示例2:
输入:n = 2
输出:false
思路
由题,所有非快乐数计算后都会陷入死循环,即求得的sum一定会有重复值。
而快乐数的sum不会有重复值。
涉及出现次数,考虑用哈希表,又要求元素不能重复,考虑使用HashSet。
双指针法:由于所有非快乐数都会陷入死循环,所以该题可以类比 142. 环形链表 II - 力扣(LeetCode)
使用快慢指针,快指针每次移动一个距离,慢指针每次移动两个距离,如果两个指针相遇则说明不是快乐数。
题解
独立题解:
class Solution {
public boolean isHappy(int n) {
Set<Integer> hashset = new HashSet<>();
while (n != 1) {
int sum = 0;
while (n != 0) {
int a = n % 10;
n /= 10;
sum += a * a;
}
if (hashset.contains(sum)) {
return false;
} else {
hashset.add(sum);
n = sum;
}
}
return true;
}
}
快慢指针:
(偷的
class Solution {
//获取下一个值的方法
public int getNext(int n) {
int totalSum = 0;
while (n > 0) {
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public boolean isHappy(int n) {
int slow = n;
int fast = getNext(n);
//循环直到 得到1/快慢指针相遇
while (fast != 1 && slow != fast) {
//慢指针移动一次
slow = getNext(slow);
//快指针移动两次
fast = getNext(getNext(fast));
}
return fast == 1;
}
}
1.两数之和
题目
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会存在一个有效答案
思路
Java HashMap | 菜鸟教程 (runoob.com)
当需要查询一个元素是否出现过或者一个元素是否在集合里的时候,第一时间想到哈希法。
本题中不但需要存放遍历过的元素,还需要知道这个元素对应的下标,因此本题需要使用到key value结构,用key存放元素值,value存放元素下标,所以需要使用HashMap结构。
需要明确两点:
- map用来做什么
- map中key和value分别表示什么
map用来存放我们访问过的元素,因为遍历数组时,需要记录之前遍历过的元素和对应的下标,这样才能找到与当前元素之和等于target的元素。
因为返回值是下标,所以value存放数组元素下标,key存放元素值。
在遍历数组的时候,只需要在map中查询是否有和当前元素匹配的数值,如果有,就返回答案,如果没有,就把当前遍历的元素放进map中,因此map存放的是已经访问过的元素。
题解
独立题解:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashmap = new HashMap<>();
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
int a = target - nums[i];
if (hashmap.containsKey(a)) {
res[0] = i;
res[1] = hashmap.get(a);
return res;
} else {
hashmap.put(nums[i], i);
}
}
return null;
}
}
标签:202,哈希,int,元素,随想录,++,数组,new,两数
From: https://blog.csdn.net/jiabao0520/article/details/142299337