代码随想录-Day6,7 哈希表

引言

题目中判断一个元素是否出现在集合里面的时候,就要考虑哈希表
哈希表使用额外的数组,set或者是map来存放数据,才能实现快速的查找,牺牲了空间换取了时间

一、有效的字母异位词

链接: 242.有效的字母异位词

思路

因为本题的字符串只涉及字母,我们可以用一个长度为26的数组来存放各字母。先遍历一遍字符串s,s中涉及到的字母都++;再遍历字符串t,涉及到的字母都–
然后再遍历存放字母数量的数组,不为0 返回false,遍历结束,返回true

代码

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] letters = new int[26];
        for (int i = 0;i < s.length();i++){
            letters[s.charAt(i) - 'a']++;
        } 
        for (int i = 0;i < t.length();i++){
            letters[t.charAt(i) - 'a']--;
        }
        for (int i = 0;i < 26;i++){
            if (letters[i] != 0)    return false;
        }
        return true;
    }
}

在java语言中,字符串的处理不能像普通数组一样处理
s是字符串,求字符串的长度 s.length() 求字符串其中一个字符s.charAt(i)

二、两个数组的交集

链接: 349.两个数组的交集

思路

本题思路很简单
观察题目,输出结果的每个元素唯一,不考虑顺序
考虑先把nums1所有元素加入Set集合中,有去重的效果
在遍历nums2时,利用Set.contains(元素)方法,将在集合中的元素加入新的集合中(不可以用List,没有去重效果)
将新的集合转化为数组,一系列函数的转化,或者用本方法,创建新的数组,挨个加进去

代码

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<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);
            }
        }
      
        //方法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题.快乐数

思路

思路其实挺简单
把平方相加循环中的各个数加入到一个集合Set里面,如果想要加入的数,集合里面已经有了,那么说明陷入了无限循环,不可能会有符合条件的值。
可以把得到下一个数的逻辑单独抽出来。
在写代码时可以用文字把思路捋一捋,不要用脑子硬想

代码

class Solution {
    public 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 int getNextNumber(int n) {
        int res = 0;
        while (n > 0) {
            int temp = n % 10;
            res += temp * temp;
            n = n / 10;
        }
        return res;
    }
}

四、两数之和

链接: 1.两数之和

思路

本题的思路不难
观察题目发现我们应该使用Map数据结构
为了方便查找,我们在存入Map时,元素为key,元素的下标为value

根据返回值设定了长度为2的数组,用来存储两个符合条件的下标
然后在循环遍历数组时
当Map中没有target - nums[i],我们将nums[i]存起来(存target - nums[i]也可以,这样就直接对比Map中有没有这个元素了)
如果有的话,res[1]就是当前元素的下标;res[0]需要查一下

代码

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<>();
    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;
}

五、四数相加II

链接: 454.四数相加II

思路

题目看起来比较唬人,其实挺简单的
四个数组是独立的,我们可以将它们两两分组,同样使用Map的数据结构
认真分析,不要怕!

代码

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>();
        int temp;
        int res = 0;
        //统计两个数组中的元素之和,同时统计出现的次数,放入map
        for (int i : nums1) {
            for (int j : nums2) {
                temp = i + j;
                if (map.containsKey(temp)) {
                    map.put(temp, map.get(temp) + 1);
                } else {
                    map.put(temp, 1);
                }
            }
        }
        //统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                temp = i + j;
                if (map.containsKey(0 - temp)) {
                    res += map.get(0 - temp);
                }
            }
        }
        return res;
    }
}

六、赎金信

链接: 383.赎金信

思路

本题思路与字母异位词类似,同样是使用长度26的数组去记录各字母的出现次数,但是ransomNote字符串总存在magazine中没有的字符,所以我们可以检测里面有没有负数来进行判断是否符合条件
思维要灵活一点

代码

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 定义一个哈希映射数组
        int[] record = new int[26];

        // 遍历
        for(char c : magazine.toCharArray()){
            record[c - 'a'] += 1;
        }

        for(char c : ransomNote.toCharArray()){
            record[c - 'a'] -= 1;
        }
        
        // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
        for(int i : record){
            if(i < 0){
                return false;
            }
        }

        return true;
    }
}

七、三数之和

链接: 15.三数之和

思路

本题可以用回溯暴力搜索搜出来

回溯暴力搜出所有的不重复三元组,然后找出相加为0的三元组

时间复杂度较高

本题使用双指针法,思路很巧妙

首先将数组进行排序,for循环从0开始遍历,同时顶一个下标left在i + 1的位置上,定义下标right在数组结尾的位置上

nums[i] + nums[left]+nums[right] = 0

如果三者相加 > 0 则移动 right

三者相加 < 0,移动left

满足条件则加入List列表中

当left > right还不满足条件时,移动 i

代码

class Solution {
    public 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--;
                } else if (sum < 0) {
                    left++;
                } else {
                    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;
    }
}

注意去重逻辑

八、四数之和

链接: 18.四数之和

思路

15.三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况.

代码

class Solution {
    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--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        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;
    }
}

结束

这一章终于写完了,回宿舍咸鱼啦~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值