哈希表刷题笔记

哈希表刷题笔记

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;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值