7月算法训练------第七天(哈希表)解题报告

这篇博客介绍了使用哈希表解决三道算法题:970.强整数、914.卡牌分组和1497.检查数组对是否可以被k整除。对于每道题目,提供了两种不同的解题思路和对应的Java代码实现。第一题通过模拟哈希表避免重复,第二题利用哈希表统计元素出现次数寻找分组可能,第三题通过哈希表记录数组元素取模后的频率判断整除性。
摘要由CSDN通过智能技术生成

7月算法训练------第七天(哈希表)解题报告

题目类型:哈希表
题目难度:简单

第一题、970. 强整数

  1. 题目链接:970. 强整数
  2. 思路分析:
  • 暴力法1:
    我们用两个ArrayList hsxhsy模拟哈希表,实现不能添加重复元素。
    其中hsx用来存储xi次幂;hsy用来存储yj次幂。
    我们声明一个ArrayList hs,用来存储最终结果。
    然后我们遍历hsxhsy,如果他们的和小于bound,且hs中不含hsx.get(i) + hsy.get(j),则将hsx.get(i)hsy.get(j)的和存入hs中,最后返回hs

    这里我们使用ArrayList中的contians(Object o)来类似哈希表中不出现重复值。

  1. 代码:
class Solution {
    public List<Integer> powerfulIntegers(int x, int y, int bound) {
        // List<Integer> list = new ArrayList();
        List<Integer> hsx = new ArrayList();
        List<Integer> hsy = new ArrayList();
        List<Integer> hs = new ArrayList();
        if(x == 1){
            hsx.add(1);
        }else{
            for(int i = 0; (int)Math.pow(x, i) < bound; i++){
                if(!hsx.contains((int)Math.pow(x, i)))
                    hsx.add((int)Math.pow(x, i));
            }
        }
        if(y == 1){
            hsy.add(1);
        }else{
            for(int j = 0; (int)Math.pow(y, j) < bound; j++){
                if(!hsy.contains((int)Math.pow(y, j)))
                    hsy.add((int)Math.pow(y, j));
            }
        }
        for(int i = 0; i < hsx.size(); i++){
            for(int j = 0; j < hsy.size(); j++){
                if(hsx.get(i) + hsy.get(j) <= bound){
                    if(!hs.contains(hsx.get(i) + hsy.get(j)))
                        hs.add(hsx.get(i) + hsy.get(j));
                }
            }
        }
        return hs;
    }
}
  • 暴力法2:
  • 如果 x i > b o u n d x^i > bound xi>bound,那么 x i + y j x^i + y^j xi+yj也不可能小于等于 b o u n d bound bound。 对于 y j y^j yj也是同样的道理。

因此,我们只需要对于所有的 0 ≤ i , j ≤ log ⁡ x ( bound ) < 20 0 \leq i, j \leq \log_x(\text{bound})< 20 0i,jlogx(bound)<20 生成一遍答案就行了。

我们可以用一个 HashSet 来存储所有不同的答案。

代码:

class Solution {
    public List<Integer> powerfulIntegers(int x, int y, int bound) {
        Set<Integer> seen = new HashSet();
        for (int i = 0; i < 20 && Math.pow(x, i) <= bound; ++i)
            for (int j = 0; j < 20 && Math.pow(y, j) <= bound; ++j) {
                int v = (int) Math.pow(x, i) + (int) Math.pow(y, j);
                if (v <= bound)
                    seen.add(v);
            }
        return new ArrayList(seen);
    }
}

第二题、914. 卡牌分组

  1. 题目链接:914. 卡牌分组
  2. 思路分析:
    我们用一个HashMap hm来存储deck中的数和出现次数,keydeck中的数,对应的value为该key值在deck中出现的次数;
    比如:deck=[1,2,3,4,4,3,2,1]
    那么:hm={key=1, value=2; key=2, value=2; key=3, value=2; key=4, value=2};
    然后我们将value用一个数组存起来,求出最小的value值;
    我们将X2开始迭代,直到最小value值;如果其他value值与该j值的余数不为0,则用布尔数组记录该次迭代为false,说明我们当前的迭代值不满足题意;如果余数全为0,则说明能找到X值满足题意;
    当我们再次遍历这个布尔数组,只要有true,就返回true;如果全为false就返回false
  3. 代码:
class Solution {
    public boolean hasGroupsSizeX(int[] deck) {
        if(deck.length < 2){
            return false;
        }
        Map<Integer, Integer> hm = new HashMap();
        for(int i = 0; i < deck.length; i++){
            if(hm.containsKey(deck[i])){
                hm.replace(deck[i], hm.get(deck[i]), hm.get(deck[i]) + 1);
            }else{
                hm.put(deck[i], 1);
            }
        }
        int[] count = new int[hm.size()]; 
        int j = 0;
        for(int i : hm.keySet()){
            count[j] = hm.get(i);
            j++;
        }
        Arrays.sort(count);
        int x = count[0];
        j = 2;
        if(x < 2) {
            return false;
        }
        boolean[] br = new boolean[x - 1];
        for(; j <= x; j++){
            for(int i = 0; i < count.length; i++){
                if(count[i] % j != 0){
                    br[j-2] = false;
                    break;
                }else{
                    br[j-2] = true;
                }
            }
        }
        for(int i = 0; i < br.length; i++){
            if(br[i]){
                return true;
            }
        }
        return false;
    }
}

第三题、1497. 检查数组对是否可以被 k 整除

  1. 题目链接:1497. 检查数组对是否可以被 k 整除

  2. 思路分析:
    我们对每个数取模,,放进一个长度为k的数组mod中,对负数,我们按照以下规则取模:
    ((arr[i] % k) + k) % k
    这个结果就是mod的下标。
    我们分析,对于正数,以上规则与取一次模的结果其实是一样的的,所以正数也按以上规则取模。
    当取模结果为0时,说明这个数本身就能被k整除,那么我们需要找一个也能被0整除的数来与其配对,所以mod[0]要为偶数;
    对于取模不为0的,取模结果为i的要与取模结果为k-i的数量要想等,这样才能返回true

  3. 代码:

class Solution {
    public boolean canArrange(int[] arr, int k) {
        int[] mod = new int[k];
        for(int i = 0; i < arr.length; i++){
            mod[((arr[i] % k) + k) % k]++;
        }
        for(int i = 1; i < k; i++){
            if(mod[i] != mod[k - i]){
                return false;
            }
        }
        return mod[0] % 2 == 0;
    }
}

第四题、面试题 17.05. 字母与数字

  1. 题目链接:面试题 17.05. 字母与数字
  2. 思路分析:
    [“A”,“1”,“B”,“C”,“D”,“2”,“3”,“4”,“E”,“5”,“F”,“G”,“6”,“7”,“H”,“I”,“J”,“K”,“L”,“M”]
    先申请一个长度为array.length的数组memo,如果遇到数字将memo[i]赋值为-1,如果遇到字母则将memo[i]赋值为1;
    即:[1,-1,1,1,1,-1,-1,-1,1,-1,1,1,-1,-1,1,1,1,1,1,1]
    然后将其计算和:
    [1, 0, 1, 2, 3, 2, 1, 0, 1, 0, 1, 2, 1, 0, 1, 2, 3, 4, 5, 6]
    指定左右两个指针,分别从左右两边遍历数组,找到最远的两个1,返回这最远两个1之间的子序列。
  3. 代码:
class Solution {
    public String[] findLongestSubarray(String[] array) {
        int len = array.length;
        int[] memo = new int[(len << 1) + 1];
        Arrays.fill(memo, -2);
        memo[len] = -1;
        int res = 0, count = 0, begin = 0, end = 0;
        for (int i = 0; i < len; ++i) {
            boolean is_num = true;
            for (char c : array[i].toCharArray())
                if (c < '0' || c > '9') {
                    is_num = false;
                    break;
                }
            count += is_num ? -1 : 1;
            if (memo[count + len] <= -2)
                memo[count + len] = i; 
            else if (i - memo[count + len] > res) {
                begin = memo[count + len] + 1;
                end = i + 1;
                res = i - memo[count + len];
            }
        }
        return Arrays.copyOfRange(array, begin, end);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值