二的幂数组中查询范围内的乘积

184 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

🎈算法并不一定都是很难的题目,也有很多只是一些代码技巧,多进行一些算法题目的练习,可以帮助我们开阔解题思路,提升我们的逻辑思维能力,也可以将一些算法思维结合到业务代码的编写思考中。简而言之,平时进行的算法习题练习带给我们的好处一定是不少的,所以让我们一起来养成算法练习的习惯。今天练习的题目是一道比较简单的题目 -> 二的幂数组中查询范围内的乘积

题目描述

给你一个正整数 n ,你需要找到一个下标从 0 开始的数组 powers ,它包含 最少 数目的 2 的幂,且它们的和为 n 。powers 数组是 非递减 顺序的。根据前面描述,构造 powers 数组的方法是唯一的。

同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [lefti, righti] ,其中 queries[i] 表示请你求出满足 lefti <= j <= righti 的所有 powers[j] 的乘积。

请你返回一个数组 answers ,长度与 queries 的长度相同,其中 answers[i]是第 i 个查询的答案。由于查询的结果可能非常大,请你将每个 answers[i] 都对 109 + 7 取余 。

示例 1:

输入:n = 15, queries = [[0,1],[2,2],[0,3]]
输出:[2,4,64]
解释:
对于 n = 15 ,得到 powers = [1,2,4,8] 。没法得到元素数目更少的数组。
第 1 个查询的答案:powers[0] * powers[1] = 1 * 2 = 2 。
第 2 个查询的答案:powers[2] = 4 。
第 3 个查询的答案:powers[0] * powers[1] * powers[2] * powers[3] = 1 * 2 * 4 * 8 = 64 。
每个答案对 109 + 7 得到的结果都相同,所以返回 [2,4,64]

示例 2:

输入:n = 2, queries = [[0,0]]
输出:[2]
解释:
对于 n = 2, powers = [2] 。
唯一一个查询的答案是 powers[0] = 2 。答案对 10^9 + 7 取余后结果相同,所以返回 [2]

提示:

  • 1 <= n <= 10^9
  • 1 <= queries.length <= 10^5
  • 0 <= starti <= endi < powers.length

思路分析

首先我们要先理解一下题目的意思,题目会给我们一个正整数 n ,我们需要找到一个下标从 0 开始的数组 powers ,它包含 最少 数目的 2 的幂,且它们的和为 n。得到powers数组之后,我们需要根据给出的二维整数数组 queries 进行相应的计算,其中 queries[i] = [lefti, righti] ,我们需要求出满足 lefti <= j <= righti 的所有 powers[j] 的乘积。

  • 1、获取powers数组

powers数组需要满足以下两个条件 - 它包含 最少 数目的 2 的幂 - 它们的和为 n  所以我们可以先找出小于等于n的所有正整数

const powList = [];
let num = 1;
while(num <= n){
    powList.push(num);
    num *= 2;
}

优先取powList中的最大值,这样可以用最少数目的整数来组成powers数组

let ind = powList.length - 1;
while(n > 0){
    while(powList[ind] > n) ind--;
    n -= powList[ind];
    powers.unshift(ind);
}
  • 2、计算乘积

我们初中的时候都学过幂运算,这里只需要用到一个简单的运算法则:

指数相乘运算公式:a^m·a^n=a^(m+n)。指数是幂运算aⁿ(a≠0)中的一个参数,a为底数,n为指数,指数位于底数的右上角,幂运算表示指数个底数相乘。当n是一个正整数,aⁿ表示n个a连乘。当n=0时,aⁿ=1。 幂运算是一种关于幂的数学运算。同底数幂相乘,底数不变,指数相加。同底数幂相除,底数不变,指数相减。幂的乘方,底数不变,指数相乘。

所以我们只需要计算指数和再求幂即可。

for(let i = 0; i < queries.length; i++){
    let ind = 0;
    for(let j = queries[i][0]; j <= queries[i][1]; j++) ind += powers[j];
    res.push(quickPow(2,ind));
}

完整AC代码如下:

AC代码

/**
 * @param {number} n
 * @param {number[][]} queries
 * @return {number[]}
 */
 var productQueries = function(n, queries) {
     const res = [];
     const powers = [];
     const powList = [];
     let num = 1;
     const mod = BigInt(1000000007);
     const quickPow = function(a, b) {
        a = BigInt(a);
        b = BigInt(b);
        let ret = BigInt(1);
        a = a % mod;
        while (b) {
            if (b & BigInt(1)) ret = ret * a % mod;
            a = a * a % mod;
            b = b >> BigInt(1);
        }
        return ret;
    };
     while(num <= n){
         powList.push(num);
         num *= 2;
     }
     let ind = powList.length - 1;
     while(n > 0){
         while(powList[ind] > n) ind--;
         n -= powList[ind];
         powers.unshift(ind);
     }
     for(let i = 0; i < queries.length; i++){
         let ind = 0;
         for(let j = queries[i][0]; j <= queries[i][1]; j++) ind += powers[j];
         res.push(quickPow(2,ind));
     }
     return res;
 };

说在后面

🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。