这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
剪绳子 II
剑指Offer 14 - II. 剪绳子 II
给你一根长度为n的绳子,请把绳子剪成整数长度的m段(m、n都是整数,n > 1并且m > 1),每段绳子的长度记为k[0],k[1]...k[m-1]。请问k[0] * k[1] * ... * k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回1。
示例1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:2 <= n <= 1000
题解
法一 BigInt + 动态规划
与上题《剪绳子》不同的是,本题需要涉及“大数越界的求余问题”。因为Math.max不能求BigInt类型的最值,所以我们可以自己写个函数判断最值。
/**
* @param {number} n
* @return {number}
*/
var cuttingRope = function (n) {
let dp = new Array(n+1).fill(BigInt(1));
for (let i = 3; i <= n; i++) {
for (let j = 1; j < i; ++j) {
dp[i] = max(dp[i], dp[j] * BigInt((i - j)), BigInt(j * (i - j)));
}
}
return dp[n] % (1000000007n);
};
const max = (...args) => args.reduce((prev, curr) => prev > curr ? prev : curr)
法二
- 一般求幂计算可以用Math.pow()
- 如果极大值的情况,那么用for循环替换,并对其进行取模运算,这样就不会造成值的溢出
/**
* @param {number} n
* @return {number}
*/
var cuttingRope = function(n){
if(n===2) return 1;
if(n===3) return 2;
// a的含义:n能拆成的3的个数
const a = Math.floor(n/3);
const b = n % 3;
let max = 1;
// n是3的倍数
if(b===0){
// Math.pow(3,a);
for(let i = 0;i < a;i++){
max = 3 * max % (1e9+7);
}
return max;
}
// n是3k+1
if(b===1) {
max = 4;
for(let i = 0;i < a-1;i++){
max = 3 * max % (1e9+7);
}
return max;
}
max = 2;
for(let i = 0;i < a;i++){
max = 3 * max % (1e9+7);
}
return max;
}
二进制中1的个数
剑指Offer 15.二进制中1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字为‘1‘的个数(也被称为汉明重量)。
示例1:
输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例2:
输入:n = 128 (控制台输入 00000000000000000000000010000000)
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例3:
输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
提示:输入必须是长度为32的二进制串
题解
法一 运用api
/**
* @param {number} n - a positive integer
* @return {number}
*/
var hammingWeight = function(n) {
return n.toString(2).split('0').join('').length;
};
法二 循环检查二进制位
直接循环检查给定整数n的二进制的每一位是否为1。
当检查第i位时,我们可以让n与2^i进行与运算,当且仅当n的第i位为1时,运算结果不为0。
1 << 2 即 0001 -> 0010
var hammingWeight = function(n) {
let len = 0;
for(let i = 0;i < 32;i++){
if((n & (1 << i))!==0){ // 判断n的第i位是否不为0
len++;
}
}
return len;
};
时间复杂度O(k) (k=32),空间复杂度O(1)
法三 位运算优化
观察这个运算:n&(n- 1),其预算结果恰为把n的二进制中的最低位1变为n之后的结果。
如:6&(6-1)=4,二进制6=(110),4=(100),运算结果4即为把6的二进制位中的最低位的1变为0之后的结果。
这样我们可以利用这个位运算的性质加速我们的检查过程,在实际代码中,我们不断让当前的n与n-1做与运算,直到n变为0即可。因为每次运算会使得n的最低位的1被翻转,因此运算次数就等于n的二进制中1的个数。
var hammingWeight = function(n){
let ret = 0;
while(n){
n &= n-1;
ret++;
}
return ret;
}
时间复杂度:O(logN)。循环次数等于n的二进制位中1的个数,最坏情况下n的二进制位全部为1。我们需要循环次。
空间复杂度:O(1)
坚持每日一练!前端小萌新一枚,希望能点个赞哇~