We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
首发于微信公众号《前端成长记》,写于 2021.01.11
本文记录刷题过程中的整个思考过程,以供参考。主要内容涵盖:
题目地址
给定一个Excel表格中的列名称,返回其相应的列序号。
例如,
A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ...
示例:
输入: "A" 输出: 1 输入: "AB" 输出: 28 输入: "ZY" 输出: 701
这道题其实就是上面168题的逆推导,也是就26进制转10进制。所以解法也很直接,做转换即可。无非区别在于正序和倒序处理罢了。
Ⅰ.正序转换
代码:
/** * @param {string} s * @return {number} */ var titleToNumber = function(s) { let res = 0 for(let i = 0; i < s.length; i++) { res = res * 26 + (s.charCodeAt(i) - 'A'.charCodeAt() + 1) } return res };
结果:
O(n)
Ⅱ.倒序转换
/** * @param {string} s * @return {number} */ var titleToNumber = function(s) { let res = 0 let mul = 1 for(let i = s.length - 1; i >= 0; i--) { res += (s.charCodeAt(i) - 'A'.charCodeAt() + 1) * mul mul *= 26 } return res };
看了一下解法,大部分都是常规的倒序解法。正序的话其实就是秦九韶算法。
这道题没有什么难度,就是一个进制转换的过程,比较容易想到的也就是倒序遍历,转换求解。
给定一个整数 n,返回 n! 结果尾数中零的数量。
n
n!
输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零。 输入: 5 输出: 1 解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。
这道题明显不适合算出来阶乘的值再来算有多少个零,因为很容易溢出,且复杂度高。所以这里需要找到规律,这里规律还很明显,零的产生一定是由 2x5(4看作2x2),所以本质上还是看 5 的数量,当然 5^n 还需要再累加。
2x5(4看作2x2)
5
5^n
Ⅰ.累加5的每个幂的个数
/** * @param {number} n * @return {number} */ var trailingZeroes = function(n) { let res = 0 while (n) { n = n / 5 | 0 res += n } return res };
O(log(n))
其实除5也可以等同于 xxx/5 + xxx/25 + xxx/125 + xxx/5^n,当5^n超过阶乘数的时候,必然取0。
xxx/5 + xxx/25 + xxx/125 + xxx/5^n
/** * @param {number} n * @return {number} */ var trailingZeroes = function(n) { let res = 0 let num = 5 while (n >= num) { res += n / num | 0 num *= 5 } return res };
没有看见更优的算法,但是看到一个偏硬核的,其实也就是上面的解法的提前枚举。因为我们之前数值运算有MAX边界,所以5^n是可枚举。
Ⅰ.枚举
/** * @param {number} n * @return {number} */ var trailingZeroes = function(n) { return (n/5 | 0)+(n/25 | 0)+(n/125 | 0)+(n/625 | 0)+(n/3125 | 0)+(n/15625 | 0)+(n/78125 | 0)+(n/390625 | 0) +(n/1953125 | 0)+(n/9765625 | 0)+(n/48828125 | 0)+(n/244140625 | 0)+(n/1220703125 | 0); };
枚举的方式纯属一乐,这道题的本质还是找到5的关键点,将问题进行转换求解即可。
颠倒给定的 32 位无符号整数的二进制位。
输入: 00000010100101000001111010011100 输出: 00111001011110000010100101000000 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 输入:11111111111111111111111111111101 输出:10111111111111111111111111111111 解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, 因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。
提示:
进阶: 如果多次调用这个函数,你将如何优化你的算法?
看到这道题,明显关键点就在各种位运算符的使用。但是也可以取巧通过字符串处理,所以下面就尝试从这两个方向去作答。
Ⅰ.位运算
/** * @param {number} n - a positive integer * @return {number} - a positive integer */ var reverseBits = function(n) { let t = 32, r = 0; while(t--) { r = (r << 1) + (n & 1); // 输出结果左移并加上最后一位 n >>= 1 // 待处理数字右移 } return r >>> 0; };
O(1)
Ⅱ.字符串处理
/** * @param {number} n - a positive integer * @return {number} - a positive integer */ var reverseBits = function(n) { return parseInt( n.toString(2).split('').reverse().join('').padEnd(32, 0), 2 ) };
这里还看见另外一种位运算符的使用
Ⅰ.位移+换位
/** * @param {number} n - a positive integer * @return {number} - a positive integer */ var reverseBits = function(n) { let t = 32, r = 0; while(t--) { r = (r << 1) | (n & 1); // 输出结果左移并末位替换 n >>= 1 // 待处理数字右移 } return r >>> 0; };
这题的考点也就在位运算了,熟悉熟悉很容易就能处理。不过熟悉位运算符对于一些运算来讲还是非常有意义的。
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 1 的个数(也被称为汉明重量)。
1
输入:00000000000000000000000000001011 输出:3 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 输入:00000000000000000000000010000000 输出:1 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 输入:11111111111111111111111111111101 输出:31 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
做了上道题之后,这道题,还可以使用位运算符来处理。同样,通过字符串获取也可以实现,但这显然不是这题的考点。
/** * @param {number} n - a positive integer * @return {number} */ var hammingWeight = function(n) { let t = 32, r = 0; while(t--) { if (n & 1 === 1) r++; // 判断最后一位 n >>= 1 // 待处理数字右移 } return r; };
/** * @param {number} n - a positive integer * @return {number} */ var hammingWeight = function(n) { return n.toString(2).replaceAll('0', '').length };
实现方法就太多了,大同小异,既然考位运算,那就再换成左移作答吧。其他的思路也都大同小异,无非就是加减、几位运算的区别。
Ⅰ.位运算-1左移
/** * @param {number} n - a positive integer * @return {number} - a positive integer */ var reverseBits = function(n) { let t = 32, r = 0; while(t--) { if ((n & (1 << (32 - t))) != 0) { r++; } } return r; };
说白了如果做了上道题之后,这题就没有什么价值了,只是写法问题,解法可太多了。
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。 输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
这道题可以转化一下,简化之后其实就是奇数项和偶数项的和的比较。最基础的思路就是分别求和,然后记录当前比较结果后再继续分别求和。如果每次利用上每次的结果的话,那就变成了一个动态规划求解的问题了,所以这道题我们循序渐进来作答。
Ⅰ.分别求和
/** * @param {number[]} nums * @return {number} */ var rob = function(nums) { let evenTotal = 0, oddTotal = 0; for(let i = 0; i < nums.length; i++) { if (i & 1) { evenTotal += nums[i] evenTotal = Math.max(evenTotal, oddTotal) // 取两种方式的更大值 } else { oddTotal += nums[i] oddTotal = Math.max(evenTotal, oddTotal) // 取两种方式的更大值 } } return Math.max(evenTotal, oddTotal) };
Ⅱ.动态规划
/** * @param {number[]} nums * @return {number} */ var rob = function(nums) { const len = nums.length; if (len === 0) return 0 if (len === 1) return nums[0] if (len === 2) return Math.max(nums[0], nums[1]) // dp[i] 表示偷i+1间的最大收益 let dp = [nums[0], Math.max(nums[0], nums[1])] for(let i = 2; i < len; i++) { dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]) } return dp[nums.length - 1] };
看了一下解法,基本都是动态规划求解。
这道题如果看多了,基本能直接转化成一个动态规划的问题,有点类似于买卖股票。但是这题我更建议分别求和每次做比较的方式,这样的话其实空间复杂度是明显更低的。
(完)
本文为原创文章,可能会更新知识点及修正错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验 如果能给您带去些许帮助,欢迎 ⭐️star 或 ✏️ fork (转载请注明出处:https://chenjiahao.xyz)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
背景
本文记录刷题过程中的整个思考过程,以供参考。主要内容涵盖:
目录
Easy
171.Excel表列序号
题目地址
题目描述
给定一个Excel表格中的列名称,返回其相应的列序号。
例如,
示例:
题目分析设想
这道题其实就是上面168题的逆推导,也是就26进制转10进制。所以解法也很直接,做转换即可。无非区别在于正序和倒序处理罢了。
编写代码验证
Ⅰ.正序转换
代码:
结果:
O(n)
Ⅱ.倒序转换
代码:
结果:
O(n)
查阅他人解法
看了一下解法,大部分都是常规的倒序解法。正序的话其实就是秦九韶算法。
思考总结
这道题没有什么难度,就是一个进制转换的过程,比较容易想到的也就是倒序遍历,转换求解。
172.阶乘后的零
题目地址
题目描述
给定一个整数
n
,返回n!
结果尾数中零的数量。示例:
说明: 你算法的时间复杂度应为 O(log n) 。
题目分析设想
这道题明显不适合算出来阶乘的值再来算有多少个零,因为很容易溢出,且复杂度高。所以这里需要找到规律,这里规律还很明显,零的产生一定是由
2x5(4看作2x2)
,所以本质上还是看5
的数量,当然5^n
还需要再累加。编写代码验证
Ⅰ.累加5的每个幂的个数
代码:
结果:
O(log(n))
其实除5也可以等同于
xxx/5 + xxx/25 + xxx/125 + xxx/5^n
,当5^n超过阶乘数的时候,必然取0。代码:
结果:
O(log(n))
查阅他人解法
没有看见更优的算法,但是看到一个偏硬核的,其实也就是上面的解法的提前枚举。因为我们之前数值运算有MAX边界,所以5^n是可枚举。
Ⅰ.枚举
代码:
结果:
O(log(n))
思考总结
枚举的方式纯属一乐,这道题的本质还是找到5的关键点,将问题进行转换求解即可。
190.颠倒二进制位
题目地址
题目描述
颠倒给定的 32 位无符号整数的二进制位。
示例:
提示:
进阶:
如果多次调用这个函数,你将如何优化你的算法?
题目分析设想
看到这道题,明显关键点就在各种位运算符的使用。但是也可以取巧通过字符串处理,所以下面就尝试从这两个方向去作答。
编写代码验证
Ⅰ.位运算
代码:
结果:
O(1)
Ⅱ.字符串处理
代码:
结果:
O(1)
查阅他人解法
这里还看见另外一种位运算符的使用
Ⅰ.位移+换位
代码:
结果:
O(1)
思考总结
这题的考点也就在位运算了,熟悉熟悉很容易就能处理。不过熟悉位运算符对于一些运算来讲还是非常有意义的。
191.位1的个数
题目地址
题目描述
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为
1
的个数(也被称为汉明重量)。示例:
提示:
进阶:
如果多次调用这个函数,你将如何优化你的算法?
题目分析设想
做了上道题之后,这道题,还可以使用位运算符来处理。同样,通过字符串获取也可以实现,但这显然不是这题的考点。
编写代码验证
Ⅰ.位运算
代码:
结果:
O(1)
Ⅱ.字符串处理
代码:
结果:
O(1)
查阅他人解法
实现方法就太多了,大同小异,既然考位运算,那就再换成左移作答吧。其他的思路也都大同小异,无非就是加减、几位运算的区别。
Ⅰ.位运算-1左移
代码:
结果:
O(1)
思考总结
说白了如果做了上道题之后,这题就没有什么价值了,只是写法问题,解法可太多了。
198.打家劫舍
题目地址
题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例:
提示:
题目分析设想
这道题可以转化一下,简化之后其实就是奇数项和偶数项的和的比较。最基础的思路就是分别求和,然后记录当前比较结果后再继续分别求和。如果每次利用上每次的结果的话,那就变成了一个动态规划求解的问题了,所以这道题我们循序渐进来作答。
编写代码验证
Ⅰ.分别求和
代码:
结果:
O(n)
Ⅱ.动态规划
代码:
结果:
O(n)
查阅他人解法
看了一下解法,基本都是动态规划求解。
思考总结
这道题如果看多了,基本能直接转化成一个动态规划的问题,有点类似于买卖股票。但是这题我更建议分别求和每次做比较的方式,这样的话其实空间复杂度是明显更低的。
(完)
The text was updated successfully, but these errors were encountered: