今天开始学习哈希表了,给我的感觉就是把所有数据存在表里,然后通过索引来查找比对数据。这样的优点是可以把时间复杂度O(n)的题目,只需要O(1)就可以做到。
但是如果给入的数值大于哈希表的大小,就引入了一个新的概念叫做哈希碰撞。
第一种办法就是在冲突处使用链表,就可以把小李和小王的数据都存在索引为1的链表中。第二种就是要求tableSize大于dataSize的时候,哈希表可以将冲突的位置中放入一个数,然后向下找一个空位放置另外一个冲突的数。
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
开始做题目:242 Valid Anagram
第一题有两种解法,一种是直接使用sort排序的方法:解法如下:
class Solution {
public boolean isAnagram(String s, String t) {
char[] sChars = s.toCharArray();
char[] tChars = t.toCharArray();
Arrays.sort(sChars);
Arrays.sort(tChars);
return Arrays.equals(sChars, tChars);
}
}
但是这种方法泛用性不强。
第二种是哈希表:
class Solution {
public boolean isAnagram(String s, String t) {
Map count = new HashMap<>();
for (char x : s.toCharArray()) {
count.put(x, count.getOrDefault(x, 0) + 1);
}
for (char x : t.toCharArray()) {
count.put(x, count.getOrDefault(x, 0) - 1);
}
for (int val : count.values()) {
if (val != 0) {
return false;
}
}
return true;
}
}
以及哈希表的数组形式:
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'] ++;
}
for(int i = 0; i
第二题是 349. Intersection of Two Arrays
也是有两种做法,一种做法是因为题目范围的原因,可以用数组来做,因为本题范围是
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 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;
}
}
第二种是用hash set:
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 set1 = new HashSet<>();
Set 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.Happy Number
这题主要的难点是计算十位和个位的平方数。还有就是是一个无限循环的 sum 直到出现1,所以hash set就很好用。
class Solution {
public boolean isHappy(int n) {
Set 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;
}
}
然后我还看到了别人写的快慢指针的解法,很好的思路,值得学习。
import java.util.LinkedList;
class Solution {
public boolean isHappy(int n) {
int slow = n;
int fast = n;
//while loop is not used here because initially slow and
//fast pointer will be equal only, so the loop won't run.
do {
//slow moving one step ahead and fast moving two steps ahead
slow = square(slow);
fast = square(square(fast));
} while (slow != fast);
//if a cycle exists, then the number is not a happy number
//and slow will have a value other than 1
return slow == 1;
}
//Finding the square of the digits of a number
public int square(int num) {
int ans = 0;
while(num > 0) {
int remainder = num % 10;
ans += remainder * remainder;
num /= 10;
}
return ans;
}
}
第四题: 1. Two Sum
这题需要做的操作是建造一个hash map然后遍历数组去查询数组中的元素,然后输出数组元素对应的下标。代码如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
if(nums == null || nums.length == 0) {
return res;
}
Map map = new HashMap<>();
for(int i = 0; i < nums.length; i++) {
int temp = target - nums[i];
if(map.containsKey(temp)) {
res[1] = i;
res[0] = map.get(temp);
break;
}
map.put(nums[i], i);
}
return res;
}
}