哈希表刷题笔记
1.LeetCode242有效的字母异位词——easy
解题思路
由于异位词都是由各个字符重复组成,所以为想到采用哈希表来存储数据方便比较,但是直接比较两个哈希表麻烦,所以想到先记录所需要比较的字符串,再用target字符串进行遍历删减哈希表,看最终哈希表的size是否为0即可,当然要是初始化好的哈希表不存在某一个target中的字符,可以直接退出遍历返回false。代码如下:
代码实现
class Solution {
public boolean isAnagram(String s, String t) {
Map<Character,Integer> need = new HashMap<>();
// 初始化哈希表
for (char item : s.toCharArray()) {
need.put(item, need.getOrDefault(item, 0) + 1);
}
for (char item : t.toCharArray()) {
if (need.containsKey(item)) {
need.put(item, need.get(item) - 1);
if (need.get(item) == 0) {
need.remove(item);
}
}
else {
return false;
}
}
if (need.size() == 0) {
return true;
}
else {
return false;
}
}
}
拓展——数组代替哈希表
由于用哈希表大材小用,实际上可以采用数组记录即可,当然这也算是哈希表的另类实现。
class Solution {
public boolean isAnagram(String s, String t) {
int[] nums = new int[26];
for (char item : s.toCharArray()) {
nums[item - 'a']++;
}
for (char item : t.toCharArray()) {
nums[item - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (nums[i] != 0) {
return false;
}
}
return true;
}
}
拓展——排序
由于异位词本身经过排序之后是完全一致的,所以先排序再进行比较即可。
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] char1 = s.toCharArray();
char[] char2 = t.toCharArray();
Arrays.sort(char1);
Arrays.sort(char2);
return Arrays.equals(char1, char2);
}
}
2.LeetCode349两个数组的交集——easy
解题思路
最简单的方法就是利用哈希表set存储元素,检查另外一个数组与其交集,再存储到新的set中,最后转化为数组返回。具体代码如下,非常简单清晰的思路。
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<>();
// set2用来存储交集元素
Set<Integer> set2 = new HashSet<>();
int j = 0;
for (int i : nums1) {
set1.add(i);
}
for (int i : nums2) {
if (set1.contains(i)) {
set2.add(i);
}
}
int[] nums3 = new int[set2.size()];
for (int i : set2) {
nums3[j++] = i;
}
return nums3;
}
}
拓展——排序加二分查找
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set = new HashSet<>();
Arrays.sort(nums1);
for (int i : nums2) {
if (binarySearch(i, nums1)) {
set.add(i);
}
}
int[] nums3 = new int[set.size()];
int j = 0;
for (int i : set) {
nums3[j++] = i;
}
return nums3;
}
private boolean binarySearch(int num, int[] nums1) {
int left = 0;
int right = nums1.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums1[mid] == num) {
return true;
}
else if (nums1[mid] > num) {
right = mid - 1;
}
else if (nums1[mid] < num) {
left = mid + 1;
}
}
return false;
}
}
3.LeetCode202快乐数——easy
代码实现
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
while (true) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
if (!set.contains(sum)) {
set.add(sum);
}
else if (set.contains(sum)) {
return false;
}
n = sum;
}
}
private int getSum(int n) {
int sum = 0;
while (n != 0) {
sum += (n % 10) * (n % 10);
n = n / 10;
}
return sum;
}
}
拓展——快慢指针
找出循环:“快指针” 每次走两步,“慢指针” 每次走一步,当二者相等时,即为一个循环周期。此时,判断是不是因为 1 引起的循环,是的话就是快乐数,否则不是快乐数。注意:此题其实不建议用集合记录每次的计算结果来判断是否进入循环,因为这个集合可能大到无法存储;另外,也不建议使用递归,同理,如果递归层次较深,会直接导致调用栈崩溃。不要因为这个题目给出的整数是 int 型而投机取巧。
class Solution {
public boolean isHappy(int n) {
int slow = n;
int fast = n;
// 由于刚开始快慢指针同时指向初始的n,所以得用do-while结构先使得fast多走一步。
do {
slow = getSum(slow);
fast = getSum(fast);
fast = getSum(fast);
} while (slow != fast);
return slow == 1;
}
private int getSum(int n) {
int sum = 0;
while (n != 0) {
sum += (n % 10) * (n % 10);
n = n / 10;
}
return sum;
}
}
4.LeetCode454四数相加 II——medium
代码实现
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int valid = 0;
for (int a : nums1) {
for (int b : nums2) {
map.put(a + b, map.getOrDefault(a + b, 0) + 1);
}
}
for (int c : nums3) {
for (int d : nums4) {
if (map.containsKey(0 - (c + d))) {
valid += map.get(0 - (c + d));
}
}
}
return valid;
}
}
5.LeetCode383赎金信——easy
哈希表记录值实现
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
Map<Character, Integer> map = new HashMap<>();
for (char ch : magazine.toCharArray()) {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
for (char ch : ransomNote.toCharArray()) {
if (map.containsKey(ch)) {
if (map.get(ch) >= 0) {
map.put(ch, map.get(ch) - 1);
}
if (map.get(ch) < 0) {
return false;
}
}
else {
return false;
}
}
return true;
}
}
拓展——数组代替哈希表
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] record = new int[26];
for (char ch : magazine.toCharArray()) {
record[ch - 'a']++;
}
for (char ch : ransomNote.toCharArray()) {
record[ch - 'a']--;
if (record[ch - 'a'] < 0) {
return false;
}
}
return true;
}
}
6.nsum问题集中讨论
6.1 TwoSum
如果只需要返回元素的值,那么采用双指针进行二分查找即可。具体代码如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
Arrays.sort(nums);
int[] nums1 = new int[] {0};
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int sum = nums[left] + nums[right];
if (sum > target) {
right--;
}
else if (sum < target) {
left++;
}
else if (sum == target) {
return new int[] {nums[left], nums[right]};
}
}
return nums1;
}
}
上题很基础,因为元素不允许重复,所以不需要考虑。在这里进行改动一下。如果数组内存在重复元素。这里就得考虑去重的问题。只需要在while循环里面使得左右指针跳过重复元素即可。代码如下:
class Solution {
public List<List<Integer>> TwoSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int sum = nums[left] + nums[right];
// 记录初始值
int leftElement = nums[left];
int rightElement = nums[right];
if (sum > target) {
right--;
}
else if (sum < target) {
left++;
}
else if (sum == target) {
res.add(new ArrayList<Integer>(Arrays.asList(leftElement, rightElement)));
while (left < right && nums[left] == leftElement) {
left++;
}
while (left < right && nums[right] == rightElement) {
right--;
}
}
}
return res;
}
}
6.2ThreeSum
根据上述问题解决3sum问题其实很简单,先确定第一个数,另外一个数就是target减去第一个数,即转化为2sum问题。代码如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
// 找出第一个数
for (int i = 0; i < nums.length; i++) {
List<List<Integer>> newRes = TwoSum(nums, i + 1, 0 - nums[i]);
for (List<Integer> newRe : newRes) {
newRe.add(nums[i]);
res.add(newRe);
}
// 去重元素
while (i < nums.length - 1 && nums[i] == nums[i + 1]) {
i++;
}
}
return res;
}
// 对2sum代码加以改进
private List<List<Integer>> TwoSum(int[] nums, int start, int target) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
int left = start;
int right = nums.length - 1;
while (left <= right) {
int sum = nums[left] + nums[right];
// 记录初始值
int leftElement = nums[left];
int rightElement = nums[right];
if (sum > target) {
right--;
}
else if (sum < target) {
left++;
}
else if (sum == target) {
res.add(new ArrayList<Integer>(Arrays.asList(leftElement, rightElement)));
while (left < right && nums[left] == leftElement) {
left++;
}
while (left < right && nums[right] == rightElement) {
right--;
}
}
}
return res;
}
}
整合简化代码得到如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for(int k = 0; k < nums.length - 2; k++){
if(nums[k] > 0) break;
// 跳过重复元素
if(k > 0 && nums[k] == nums[k - 1]) continue;
// 定义双指针
int i = k + 1, j = nums.length - 1;
while(i < j){
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0){
while(i < j && nums[i] == nums[++i]);
} else if (sum > 0) {
while(i < j && nums[j] == nums[--j]);
} else {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i < j && nums[i] == nums[++i]);
while(i < j && nums[j] == nums[--j]);
}
}
}
return res;
}
}
6.3FourSum
四数之和同理可以写出代码。先看暴力解法:
class Solution {
public static List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> resp = new ArrayList<>();
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
for(int k=j+1;k<nums.length;k++){
for(int l=k+1;l<nums.length;l++){
if(nums[i]+nums[j]+nums[k]+nums[l] == target){
List<Integer> ans = new ArrayList<>();
ans.add(nums[i]);
ans.add(nums[j]);
ans.add(nums[k]);
ans.add(nums[l]);
Collections.sort(ans);
if(!resp.contains(ans)){
resp.add(ans);
}
}
}
}
}
}
return resp;
}
}
通过前面双指针和排序完成:
class Solution {
public static List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for(int k = 0; k < nums.length - 2; k++){
for (int m = k + 1; m < nums.length - 2; m++) {
int i = m + 1;
int j = nums.length - 1;
while(i < j){
int sum = nums[m] + nums[k] + nums[i] + nums[j];
if(sum < target){
while(i < j && nums[i] == nums[++i]);
}
else if (sum > target) {
while(i < j && nums[j] == nums[--j]);
}
else {
// 注意去重问题
if (!res.contains(new ArrayList<Integer>(Arrays.asList(nums[k],nums[m], nums[i], nums[j])))) {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k],nums[m], nums[i], nums[j])));
}
while(i < j && nums[i] == nums[++i]);
while(i < j && nums[j] == nums[--j]);
}
}
}
}
return res;
}
}