哈希表的应用
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
1. 有效的字母异位词
题目链接:242. 有效的字母异位词 - 力扣(LeetCode)
思路:用一个数组来计数即可,遍历的时候串1
的字母个数++
,串2
的字母个数--
,最后遍历26个字母,要是cnt
都为0
说明两个串的字母完全一样,反之有问题。
Code
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
int[] cnt = new int[26];
for(int i = 0; i < s.length(); i ++){
cnt[s.charAt(i) - 'a'] ++;
cnt[t.charAt(i) - 'a'] --;
}
// 枚举所有字母,看是否都为0,若存在不为0说明两个串有的字母次数不一样
for(int i = 0; i < 26; i ++){
if(cnt[i] != 0)
return false;
}
return true;
}
}
2. 两个数组的交集
题目链接:349. 两个数组的交集 - 力扣(LeetCode)
思路:开一个哈希表,标记nums1
,用于遍历nums2
时判断nums2
的元素是否在nums1
出现过。注意交集是不能含有重复元素的
Code
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
boolean[] st = new boolean[1010];
List<Integer> tmp = new ArrayList<>();
for(int i = 0; i < nums1.length; i ++) st[nums1[i]] = true;
for(int i = 0; i < nums2.length; i ++){
if(st[nums2[i]] == true && !tmp.contains(nums2[i])){// 1在2中出现的 且不能重复
tmp.add(nums2[i]);
}
}
int[] res = new int[tmp.size()];
for(int i = 0; i < tmp.size(); i ++) res[i] = tmp.get(i);
return res;
}
}
3. 快乐数
思路一:哈希表判重
无限循环:那么必定有重复的sum
出现,本质就是判重(形成了环)
Code
// 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1
// 无限循环:那么必定有重复的sum出现,本质就是判重(形成了环)
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
while(true){
int sum = get_sum(n);
if(sum == 1){
return true;
}
if(set.contains(sum)){
return false;
}else {
set.add(sum);
}
// 为下一次准备
n = sum;
}
}
public static int get_sum(int n){
int res = 0;
while(n > 0){
int t = n % 10;
res += (t * t);
n /= 10;
}
return res;
}
}
思路二:快慢指针判断环(环形链表II)
快慢指针从起点开始,快每次走两步,慢每次走一步,当快慢指针第一次相遇(相遇)时,说明就存在了环。此时,判断是不是因为 1 引起的循环,是的话就是快乐数,否则不是快乐数。(要是是1的话,最终快慢的值也都为1,因此最后判断是不是因为 1 引起的循环即可)
Code
// 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1
// 无限循环:那么必定有重复的sum出现,本质就是判重(形成了环)
class Solution {
public boolean isHappy(int n) {
int fast = n;
int slow = n;
// 要用do-while --- 因为一开始时是一样的
do {
fast = get_sum(fast);
fast = get_sum(fast);
slow = get_sum(slow);
}while(fast != slow);
return fast == 1;
}
public static int get_sum(int n){
int res = 0;
while(n > 0){
int t = n % 10;
res += (t * t);
n /= 10;
}
return res;
}
}
4. 两数之和
思路一:暴力枚举
时间复杂度:O(n * n)
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
int cnt = 0;
int n = nums.length;
for(int i = 0; i < n; i ++){
for(int j = i + 1; j < n; j ++){
if(nums[i] + nums[j] == target){
res[cnt ++] = i;
res[cnt ++] = j;
break;
}
}
}
return res;
}
}
思路二:哈希表
暴力枚举的时间复杂度在于,固定一个x
后去找target - x
,去找target - x
这一操作的时间复杂度是O(n)
的,我们可以用哈希表来对找target - x
进行优化,使其为O(1)
这样总的时间复杂度就从O(n * n) ---> O(n)
:
- 对于每一个
x
,我们判断哈希表中是否存在targrt - x
,若存在获取target - x
值所对应的key
(位置)然后和x的位置
直接返回即可 - 反之将
x
插入哈希表即可
Code
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> mp = new HashMap<>();
for(int i = 0; i < nums.length; i ++){
if(mp.containsKey(target - nums[i])){
return new int[]{mp.get(target - nums[i]), i};
}
mp.put(nums[i], i);
}
return new int[0];
}
}