2021-07-07&08 力扣 每日一题-使用哈希表解决问题

使用哈希表的好处
  • 哈希表可以帮助我们快速存取和提取数据。
哈希表的原理
  • 设置一个哈希函数,当我们要存取数据的时候,通过哈希函数来找到对应的映射关系。

  • 就比如说:现在我要存储4个元素 13 7 14 11,显然,我们可以用数组来存。也就是:a[1] = 13; a[2] = 7; a[3] = 14; a[4] = 11;当然,我们也可以用Hash来存。下面给出一个简单的Hash存储:

    • 先来确定那个函数。我们就用h(ki) = ki%5;(这个函数不用纠结,我们现在的目的是了解为什么要有这么一个函数)。
    • 对于第一个元素 h(13) = 13%5 = 3; 也就是说13的下标为3;即Hash[3] = 13;
    • 对于第二个元素 h(7) = 7 % 5 = 2; 也就是说7的下标为2; 即Hash[2] = 7;
    • 同理,Hash[4] = 14; Hash[1] = 11;
    • 现在我要你查找11这个元素是否存在。你会怎么做呢?当然,对于数组来说,那是相当的简单,一个for循环就可以了。也就是说我们要找4次
    • 下面我们来用Hash找一下。
      首先,我们将要找的元素11代入刚才的函数中来映射出它所在的地址单元。也就是h(11) = 11%5 = 1 了。下面我们来比较一下Hash[1]=11, 这个问题就很简单了。也就是说我们就找了1次。这个就是Hash的妙处了
  • 由此,我们发现,使用哈希表可以节省时间,提高程序运行的速率,这个再刷oj的时候就很重要了

例题1

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。
你可以搭配 任意 两道餐品做一顿大餐。
给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i​​​​​​​​​​​​​​ 道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。
注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。

示例 1:
输入:deliciousness = [1,3,5,7,9]
输出:4
解释:大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。
示例 2:
输入:deliciousness = [1,1,1,3,3,3,7]
输出:15
解释:大餐的美味程度组合为 3 种 (1,1) ,9 种 (1,3) ,和 3 种 (1,7) 。
提示:
1 <= deliciousness.length <= 105
0 <= deliciousness[i] <= 220

  • 分析:最开始我的想法就是:每次在数组找两个数,然后累加,最后判断这个结果是不是2的n次幂,就可以得到结果。

  •   public static int countPairs(int[] deliciousness) {
          int res=0;
          for (int i = 0; i < deliciousness.length; i++) {
              for(int j=i+1;j<deliciousness.length;j++){
                  int a=deliciousness[i]+deliciousness[j];
                  if (check(a)){
                      res++;
                  }
              }
          }
          return res;
      }
      //判断一个数是不是2的几次幂
      public static boolean check(int num){
          return num > 0 && (num & (num - 1)) == 0;
      }}
    
  • 但是遗憾这个方法思路没有错,但是却超时了。

  • 使用哈希表分析:就是用这个数组(比如是1,1,1,3,3,3,7)能得到的最大的数(7)的两倍(14)作为一个范围(1-14),在这个范围内能得到的2的次幂的有多少个,并且这些数具体是多少(1,4,8),用这些数减去我们遍历数组得到的每一个数(num),这个数(1/4/8-num)在原来数组里面有多少个,就有多少个答案,加起来就可以了。

import java.util.*;

public class Main {
    public static void main(String[] args){
        int[] deliciousness = {1,1,1,3,3,3,7};
        System.out.println(countPairs(deliciousness));
    }
    public static int countPairs(int[] deliciousness) {
        final int MOD = 1000000007;//结果需要的取余
        int maxVal = 0;
        for (int val : deliciousness) {
            maxVal = Math.max(maxVal, val);//找到数组里面最大的一个数
        }
        int maxSum = maxVal * 2;//找到最大的范围,一直循环要最接近最大的范围

        int pairs = 0;//结果返回
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int n = deliciousness.length;//长度

        for (int i = 0; i < n; i++) {
            int val = deliciousness[i];
            for (int sum = 1; sum <= maxSum; sum <<= 1) {//算数左移,1,10,100,1000,10000
                int count = map.getOrDefault(sum - val, 0);//
                pairs = (pairs + count) % MOD;
            }
            map.put(val, map.getOrDefault(val, 0) + 1);
        }
        return pairs;
    }
}
例题2:和相同的二元子数组

给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。子数组 是数组的一段连续部分。

示例 1:
输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]

示例 2:
输入:nums = [0,0,0,0,0], goal = 0
输出:15

  • 分析:一开始是使用数组方法,遍历数组,看哪两个数相减或者是他本身符合goal的要求,就可以直接++起来,很遗憾也是超时的
  •  public static int numSubarraysWithSum(int[] nums, int goal) {
          int res=0;
          List<Integer> list=new ArrayList<>();//用来记录阶段的和
          int sum=0;//用来统计数字的和
          for (int i = 0; i < nums.length; i++) {
              sum+=nums[i];
              //从头开始的阶段和的子数组也符合要求
              if (sum==goal)
                  res++;
              //阶段性的组合也符合要求
              for (int j = 0; j < list.size(); j++) {
                  if (sum-list.get(j)==goal)
                      res++;
              }
              list.add(sum);//每一次都要添加数据到上面
          }
          return res;
      }}
    
  • 用哈希表分析:我们用哈希表记录每一种前缀和出现的次数,假设我们当前枚举到元素 nums[j],我们只需要查询哈希表中元素sum[j]−goal 的数量即可,这些元素的数量即对应了以当前 j 值为右边界的满足条件的子数组的数量。最后这些元素的总数量即为所有和为 goal 的子数组数量。在实际代码中,我们实时地更新哈希表,以防止出现 i≥j 的情况。
class Solution {
    public int numSubarraysWithSum(int[] nums, int goal) {
        int sum = 0;
        Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();
        int ret = 0;
        for (int num : nums) {
            cnt.put(sum, cnt.getOrDefault(sum, 0) + 1);
            sum += num;
            ret += cnt.getOrDefault(sum - goal, 0);
        }
        return ret;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值