哈希表理论基础
LeetCode 242.有效的字母异位词
题目链接:242.有效的字母异位词
解题思路
思路一(暴力解法)
先判断长度,如果长度不一样就输出false,一样的话我们将两个字符串变为两个char数组,再通过sort()函数按照ASCII码进行排序,然后用equal()函数比较两个数组是否一样。
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
char[] arr1 = s.toCharArray();
char[] arr2 = t.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
return Arrays.equals(arr1, arr2);
}
}
思路二(哈希表的应用—数组)
分析题目可知s 和 t 仅包含小写字母,则我们可以设置一个长度为26的数组,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。然后再对s字符串进行遍历,与之匹配的索引的值加一,结束之后,在对t字符串进行遍历,与之匹配的索引的值减一,然后对数组进行遍历,如果存在索引值不等于0的,输出false。
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
record[c -'a']++;
}
for(int i = 0; i < t.length(); i++){
char h = t.charAt(i);
record[h -'a']--;
}
for(int count : record){
if(count != 0){
return false;
}
}
return true;
}
}
思路三(哈希表的应用—map)
初始版
我们可以将创建一个map,key为字符,value为字符的个数,然后对s字符串进行遍历,添加至map中,在对t的字符串进行遍历,如果不存在就添加进去,value设为-1,存在就对该key的value-1,如果key对应的value出现了-1则直接输出false,然后在对整个map的value进行求和如果不等于0则false反之ture
class Solution {
public boolean isAnagram(String s, String t) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
for(int i = 0; i < t.length(); i++){
char h = t.charAt(i);
map.put(h, map.getOrDefault(h, 0) - 1);
if(map.get(h) < 0){
return false;
}
}
Integer totalCount = map.values().stream().mapToInt(Integer::intValue).sum();
if(totalCount > 0){
return false;
}
return true;
}
}
改进版
我开始判断两个字符串的长度是否相同,如果不相同就一定不是字母异位词,而相同且两个字符串不是字母异位词,只有两种可能:
- 存在不相同的字母
- 相同字母的个数不相同
我们可以在遍历t字符串开始时判断如果字母的不存在或者字母对应value为0就输出false,不是则将对应的value-1。
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
Map<Character, Integer> map = new HashMap<Character, Integer>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
for(int i = 0; i < t.length(); i++){
char h = t.charAt(i);
if(!map.containsKey(h) || map.get(h) == 0){
return false;
}
map.put(h, map.get(h) - 1);
}
return true;
}
}
LeetCode 349. 两个数组的交集
题目链接:349. 两个数组的交集
解题思路
这道题思路的不同主要在选取存放的哈希表的数据结构不同,分为一下三个:
- 数组:如果这道题目没有0 <= nums1[i], nums2[i] <= 1000这个条件,数组的长度就无法确定,会使得空间的浪费。
- HashSet:这道题目需要输出的数不重复,且不考虑顺序,符合set的性质
- HashMap:map的key,value能对需要对比的值进行查找。
解题思路: 如下图
思路一(哈希表的应用—数组)
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> res = new HashSet<Integer>();
int[] hash = new int[1001];
for(int i = 0; i < nums1.length; i++){
hash[nums1[i]] = 1;
}
for(int i = 0; i < nums2.length; i++){
if(hash[nums2[i]] == 1){
res.add(nums2[i]);
}
}
return res.stream().mapToInt(x -> x).toArray();
//另一种输出方式
// int[] ans = new int[res.size()];
// int j = 0;
// for(int i : res){
// ans[j] = i;
// j++;
// }
// return ans;
}
}
思路二(哈希表的应用—set)
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> res = new HashSet<Integer>();
Set<Integer> hash = new HashSet<Integer>();
for(int i = 0; i < nums1.length; i++){
hash.add(nums1[i]);
}
for(int i = 0; i < nums2.length; i++){
if(hash.contains(nums2[i])){
res.add(nums2[i]);
}
}
return res.stream().mapToInt(x -> x).toArray();
//另一种输出方式
// int[] ans = new int[res.size()];
// int j = 0;
// for(int i : res){
// ans[j] = i;
// j++;
// }
// return ans;
}
}
思路三(哈希表的应用—map)
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
int n = 0;
Set<Integer> res = new HashSet<Integer>();
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < nums1.length; i++){
map.put(nums1[i], map.getOrDefault(nums1[i], 1));
}
for(int i = 0; i < nums2.length; i++){
if(map.containsKey(nums2[i])){
res.add(nums2[i]);
}
}
return res.stream().mapToInt(x -> x).toArray();
//另一种输出方式
// int[] ans = new int[res.size()];
// int j = 0;
// for(int i : res){
// ans[j] = i;
// j++;
// }
// return ans;
}
}
LeetCode 202. 快乐数
题目链接:202. 快乐数
解题思路
思路一(哈希表的应用—set)
分析题目可知:
快乐数:
非快乐数:
上图出自:代码随想录算法训练营第六天|哈希表
代码的实现:
- 我要对数进行各位之间的平方的求和
- 求和的值是否等于一,
- 对求和的数在hashset中查找是否有相同的值,如果没有就加入到hashset中
- 结束上述循环的要么数值等于一,要么hashset出现相同值。
- 我们防止当n == 1s,没有加入循环,默认的boolean值为false时,再return时进行判断是否是n == 1。
初始版
class Solution {
public boolean isHappy(int n) {
boolean ans = false;
int sum = 0;
Set<Integer> res = new HashSet<Integer>();
while(!res.contains(n) && n!=1) {
res.add(n);
while(n > 0){
sum = sum + (n % 10) * (n % 10);
n = n / 10;
}
n = sum;
sum = 0;
if(n == 1){
ans = true;
}
}
return n == 1 ? true : ans;
}
}
改进版
class Solution {
public boolean isHappy(int n) {
Set<Integer> res = new HashSet<Integer>();
while(!res.contains(n) && n!=1) {
res.add(n);
n = getNextNumber(n);
}
return n == 1;
}
private int getNextNumber(int n) {
int sum = 0;
while(n > 0){
sum = sum + (n % 10) * (n % 10);
n = n / 10;
}
return sum;
}
}
LeetCode 1. 两数之和
题目链接:两数之和
解题思路
思路一(暴力解法)
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
for(int i = 0; i < nums.length; i++){
for(int j = i + 1; j < nums.length; j++){
if(nums[i] + nums[j] == target){
res[0] = i;
res[1] = j;
}
}
}
return res;
}
}
思路二(哈希表的应用—map)
分析题目可知:
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
我们首先想到使用哈希表(一般哈希表都是用来快速判断一个元素是否出现集合里。)
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
- 而hashmap存在key和value,我们可以用key保留数组里的值,value保存值对应的索引,然后我们可以运用到containsKey函数去查找是否存在(target-num[i]),因为数组中会存在两个数之和等于target,我们去遍历数组,将第一个数加入进map,第二个数通过map函数去查找在map所在的value,然后输出当前位置和查询到的value。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < nums.length; i++){
if(map.containsKey(target - nums[i])){
res[0] = map.get(target - nums[i]);
res[1] = i;
}
map.put(nums[i], i);
}
return res;
}
}