哈希表
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。牺牲了空间换取了时间
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set(集合)
- map(映射)
第一题:242. 有效的字母异位词 - 力扣(LeetCode)
//暴力
public static boolean isAnagram2(String s, String t) {
if (s.length() != t.length()){
return false;
}
char[] ch_s = s.toCharArray();
char[] ch_t = t.toCharArray();
Arrays.sort(ch_s);
Arrays.sort(ch_t);
//统计s中有哪些字符
for (int i = 0; i < ch_s.length; i++) {
if (ch_s[i] != ch_t[i]){
return false;
}
}
return true;
}
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()){
return false;
}
//遍历s,将s中含有的字符存储在一个数组中
int[] arr = new int[26];
char[] ch_s = s.toCharArray();
char[] ch_t = t.toCharArray();
for (int i = 0; i < ch_s.length; i++) {
//将s字符存入数组中
arr[ch_s[i] - 'a']++;
}
for (int i = 0; i < ch_t.length; i++) {
//将t字符从数组中删去
arr[ch_t[i] - 'a']--;
}
//只要数组中有一个不为0
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0){
return false;
}
}
return true;
}
第二题:349. 两个数组的交集 - 力扣(LeetCode)
public int[] intersection(int[] nums1, int[] nums2) {
//特殊情况
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> resSet = new HashSet<>();
//遍历数组1:这也是一个去重的过程
for (int i : nums1) {
set1.add(i);
}
//遍历数组2的过程中判断哈希表中是否存在该元素
for (int i : nums2) {
if (set1.contains(i)) {
resSet.add(i);
}
}
//将Set转为数组
int[] array = new int[resSet.size()];
int j = 0;
//遍历resSet,将resSet中的每一个元素存入array数组中
for (int i : resSet) {
array[j++] = i;
}
return array;
}
public static boolean isHappy(int n) {
// 记录已经计算过的数字,避免重复和无限循环
Set<Integer> record = new HashSet<>();
while (n != 1 && !record.contains(n)) {
record.add(n);
n = getNextNumber(n);
}
return n == 1;
}
private static int getNextNumber(int n) {
int res = 0;
while (n > 0) {
int temp = n % 10;
res += temp * temp;
n = n / 10;
}
return res;
}
//暴力解法
public static int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return res;
}
int[] res = new int[2];
int sum = 0;
//慢指针
for (int i = 0; i < nums.length; i++) {
//快指针
for (int j = i + 1; j < nums.length; j++) {
sum = nums[i] + nums[j];
if (sum == target) {
res[0] = i;
res[1] = j;
return res;
}
}
}
return res;
}
//Map解法
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<>(); //map:存储遍历过的元素 将元素作为key,数组下标作为value
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;
}
第五题:454. 四数相加 II - 力扣(LeetCode)
//暴力解法
public static int fourSumCount1(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int count = 0;
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
for (int k = 0; k < nums3.length; k++) {
for (int g = 0; g < nums4.length; g++) {
if (nums1[i] + nums2[j] + nums3[k] + nums4[g] == 0) {
count++;
}
}
}
}
}
return count;
}
//哈希表:map解法
public static int fourSumCount2(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//统计两个数组中的元素之和,同时统计出现的次数,放入map
for (int i : nums1) {
for (int j : nums2) {
int sum = i + j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for (int i : nums3) {
for (int j : nums4) {
int sum = i + j;
res += map.getOrDefault(-sum, 0);
}
}
return res;
}
//暴力:遍历这两个字符串,
public static boolean canConstruct(String ransomNote, String magazine) {
char[] ch_r = ransomNote.toCharArray();
char[] ch_m = magazine.toCharArray();
for (int i = 0; i < ch_r.length; i++) {
for (int j = 0; j < ch_m.length; j++) {
//遍历:如果r中的字符串在m存在,则将r中这个字符赋值为'0'
if (ch_r[i] == ch_m[j]){
ch_r[i] = '0';
ch_m[j] = '1';
break;
}
}
}
//遍历看这个ch_r是否都为'0'
for (int i = 0; i < ch_r.length; i++) {
if (ch_r[i] != '0'){
return false;
}
}
return true;
}
//哈希解法
public boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()) {
return false;
}
// 定义一个哈希映射数组
int[] record = new int[26];
char[] ch_r = ransomNote.toCharArray();
char[] ch_m = magazine.toCharArray();
//遍历magazine记录magazine里各个字符出现的次数
for (int i = 0; i < ch_m.length; i++) {
record[ch_m[i] - 'a']++;
}
//遍历ransomNote,在record数组中对应的字符个数做--操作
for (int i = 0; i < ch_r.length; i++) {
record[ch_r[i] - 'a']--;
}
//如果小于0,则证明magazine中不完全包含ransomNote
//如果大于0,则证明magazine中完全包含ransomNote,且还有多余的字母
for (int i = 0; i < record.length; i++) {
if (record[i] < 0){
return false;
}
}
return true;
}
//暴力
public static List<List<Integer>> threeSum(int[] nums) {
HashSet<List<Integer>> set = new HashSet<>();
for (int i = 0; i < nums.length - 2; i++) {
for (int j = i + 1; j < nums.length - 1; j++) {
for (int k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] == 0) {
//排序后存放在set中的集合能保证没有重复
List<Integer> list = Arrays.asList(nums[i], nums[j], nums[k]);
Collections.sort(list);
set.add(list);
}
}
}
}
return new ArrayList<>(set);
}
//双指针思想
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
//先对数组进行排序
Arrays.sort(nums);
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for (int i = 0; i < nums.length; i++) {
//排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) { //去重a
continue;
}
int left = i + 1;
int right = nums.length - 1;
//不能相等,如果相等这就只剩下两个数字了,不满足三元组
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) { //和有点大,故将right往左移动
right--;
}
if (sum < 0) { //和有点小,故将left往右移动
left++;
}
if (sum == 0){ //满足要求
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对 b 和 c 去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
//双指针思路
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return result;
}
if (i > 0 && nums[i - 1] == nums[i]) { // 对nums[i]去重
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) { // 和有点大,故将right往左移动
right--;
}
if (sum < target) { // 和有点小,故将left往右移动
left++;
}
if (sum == target) { // 满足要求
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1])
right--;
while (right > left && nums[left] == nums[left + 1])
left++;
left++;
right--;
}
}
}
}
return result;
}