【每日刷题】哈希-随想录2、3、4、5、8、LC49、LC128

  1. 随想录2、LC242 有效的字母异位词
  2. 随想录3、LC349两个数组的交集

3. 随想录4、LC202 快乐数

给一个整数,计算该数字每一位数字的平方和。核心是先拿到每一位数字,怎么拿?
int = 2579
2579 / 10 = 257 … 9
257 10 = 25 … 7
25 / 10 = 2 … 5
2 / 10 = 0 … 2
当得数>0时,循环,所有的余数则为每一位数字了。

接下来是解题。
题目中已经暗示了,只有两种情况,一是无限循环但不等于1,二是无限循环等于1。至于为什么一定会无限循环,答案是因为int类型最大值为为‭‭2 147 483 647‬‬, 所以平方和最大的数是1 999 999 999,平方和为1 + 81*9 = 724。任何数的平方和都在1到724之间,724次循环之内一定有重复的。
所以用set来记录每一次的sum,如果sum一直不重复,循环,直到有重复出现。如果重复的值是1,就是快乐数。

代码

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        n = calcuSum(n);
        if (n == 1){
            return true;
        }
        while (!set.contains(n)){
            set.add(n);
            n = calcuSum(n);
        }
        if (n == 1){
            return true;
        }else{
            return false;
        }
    }

    public int calcuSum(int n){
        int sum = 0;
        while (n > 0){
            sum += Math.pow(n%10, 2);
            n = n/10;
        }
        return sum;
    }
}
  1. 随想录5.LC1 两数之和

5. 随想录8. LC15 三数之和

题目链接

整体思路:
在这里插入图片描述

在这里插入图片描述

比较重要的几个点:

  1. 先将数组排序
  2. 为了去重,i 和 left 和 right 都需要去重:
    i 的去重: 如果重复了怎么办,i 是nums里遍历的元素,那么应该直接跳过去。但这里有一个问题,是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同。如果是判断 nums[i] 与 nums[i + 1]是否相同,那我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。所以,应为判断 nums[i] 与 nums[i-1] 是否相同。
    left 与 right 的去重:如果不加left 和 right,下图为一个错误示例:
    在这里插入图片描述
    所以应加去重逻辑:必须要保证left < right,万一减着减着,right < left了,不仅无意义,且nums有可能刨指针异常。
//left right去重
                    while (left < right && nums[left+1] == nums[left]){
                        left++;
                    }
                    while (left < right && nums[right-1] == nums[right]){
                        right--;
                    }

还有一个易错点,left 和 right 是针对for来的,所以每次for遍历,都需要重新指定left和right,再开始while。

代码

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        int left = 0; 
        int right = nums.length - 1;
        for (int i=0; i<nums.length; i++){
            //在while循环前初始化left,right
            left = i + 1;
            right = nums.length - 1;
            //i去重
            if (i > 0){
                if (nums[i] == nums[i-1]){
                    continue;
                }
            }
            while (left < right){
                List<Integer> temp = new ArrayList<>();
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0){
                    right--;
                }
                else if (sum < 0){
                    left++;
                }
                else{
                    temp.add(nums[i]);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    list.add(new ArrayList<>(temp));
                    //left right去重
                    while (left < right && nums[left+1] == nums[left]){
                        left++;
                    }
                    while (left < right && nums[right-1] == nums[right]){
                        right--;
                    }
                    left++;
                    right--;
                }
            }
            
        }
        return list;
    }
}

这道题抛开i,单看left 和 right 的while遍历,类似于需要返回数值的两数之和。
(但上题的两数之和是要求返回索引)

6. LC49.字母异位词分组

题目链接
解法一:(用例都通过,但超时了)
复用判断两个字符串是否为字母异位词的方法,即LC242
暴力for循环,嵌套,遍历。i固定,j从i+1开始遍历,如果strs[i], strs[j]互为异位词,加入temp,并在record数组中标记该字符串以和别的字符串互为异位词。j遍历结束,temp加入list。i往前移一个,继续开始下一次遍历。

代码

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> list = new ArrayList<>();
        int[] record = new int[strs.length];
        for(int i=0; i<strs.length; i++){
            if (record[i] != 0){
                continue;
            }
            List<String> temp = new ArrayList<>();
            temp.add(strs[i]);
            for (int j=i+1; j<strs.length; j++){
                if (record[j] != 0){
                    continue;
                }
                if (check(strs[i], strs[j])){
                    temp.add(strs[j]);
                    record[j] = 1;
                }
            }
            record[i] = 1;
            list.add(new ArrayList<>(temp)); 
        }
        return list;
    }

    public boolean check(String str1, String str2){
        int[] array = new int[26];
        for (int i=0; i<str1.length(); i++){
            array[str1.charAt(i)-'a']++;
        }
        for (int i=0; i<str2.length(); i++){
            array[str2.charAt(i)-'a']--;
        }
        for (int num: array){
            if (num != 0){
                return false;
            }
        }
        return true;
    }
}

上述解法耗时:主要是判断两个字符串是否异位词,要遍历两遍。2n
又暴力解法,n^2

解法二:
考虑哈希。
异位词的两个字符串排序后一定相等。则维护一个map,map的key为排序后的字符串,value为排序后与key相等的字符串list。

易错!
map.put(sNew, new ArrayList<>(temp));

  1. key要放的是排序后的sNew,而不是s
  2. value要新new ArrayList<>(temp)
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (String s: strs){
        	//字符串排序
            char[] array = s.toCharArray();
            Arrays.sort(array);
            String sNew = new String(array);
            
            // 如果map中存在排序相同的字符串,将当前字符串添加到对应的列表中
            if (map.containsKey(sNew)){
                map.get(sNew).add(s);
            }
            //若不存在,将排序后的字符串作为key,当前字符串作为value,放进map中
            else{
                List<String> temp = new ArrayList<>();
                temp.add(s);
                map.put(sNew, new ArrayList<>(temp));
            }
        }
        return new ArrayList<>(map.values());
    }    
}
class Solution {
    public int longestConsecutive(int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        Arrays.sort(nums);
        int max = 1;
        int[] dp = new int[nums.length];
        dp[0] = 1;
        for (int i=1; i<nums.length; i++){
            if (nums[i] == nums[i-1]+1){
                dp[i] = dp[i-1] + 1;
                if (dp[i] > max){
                    max = dp[i];
                }
            }else{
                dp[i] = 1;
            }
        }
        return max;
    }
}

7. LC128.最长连续序列

理解题意:
输入:nums1 = [0,3,7,2,5,8,4,6,0,1]。输出:9 。连续序列:0123456
输入:nums2 = [1,2,0,1]。输出:3 。连续序列:012

解法一:动规
先对数组排序。
dp[i]表示以i结尾的连续最长序列的长度。
nums2排序完为[0,1,1,2]。但因为不要求序列元素在数组中连续,所以其连续序列为012,长度为3。

所以三种情况:

  1. 如果nums[i] == nums[i-1]+1,则连续,dp[i] = dp[i-1] + 1
  2. 如果nums[i] == nums[i-1],则连续但不贡献连续长度,dp[i] = dp[i-1]
  3. 其他,dp[i] = 1

其间维护max。最大长度。

代码

class Solution {
    public int longestConsecutive(int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        Arrays.sort(nums);
        int max = 1;
        int[] dp = new int[nums.length];
        dp[0] = 1;
        for (int i=1; i<nums.length; i++){
            if (nums[i] == nums[i-1]+1){
                dp[i] = dp[i-1] + 1;
                if (dp[i] > max){
                    max = dp[i];
                }
            }
            else if (nums[i] == nums[i-1]){
                dp[i] = dp[i-1];
            }
            else{
                dp[i] = 1;
            }
        }
        return max;
    }
}

解法二:哈希
在这里插入图片描述

class Solution {
    public int longestConsecutive(int[] nums) {
        int res = 0;    // 记录最长连续序列的长度
        Set<Integer> numSet = new HashSet<>();  // 记录所有的数值
        // 将数组中的值加入哈希表中
        for(int num: nums){
            numSet.add(num);    
        }
        
        int seqLen;     // 连续序列的长度
        for(int num: numSet){
            // 如果当前的数是一个连续序列的起点,统计这个连续序列的长度
            if(!numSet.contains(num - 1)){
                seqLen = 1;
                // 不断查找连续序列,直到num的下一个数不存在于数组中
                num++;
                while(numSet.contains(num)){
					seqLen++;
					num++;
				}  
                res = Math.max(res, seqLen);    // 更新最长连续序列长度
            }
        }
        return res;
    }
}
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值