符号统计
LeetCode1822 给定一个数组,求所有元素的乘积的符号,如果最终答案是负的返回-1,如果最终答案是正的返回1,如果答案是0返回0。
仔细分析一下这道题目,如果将所有数都乘起来,再判断正负,工作量真不少,而且还可能溢出。我们发现,一个数如果是 -100 和 -1,对符号位的贡献是完全一样的,所以只需要看有多少个负数,就能够判断最后乘积的符号了。
public int arraySign(int[] nums) {
int prod = 1;
for(int i = 0; i < nums.length; ++i) {
if(nums[i] == 0) {
return 0;//一切归零
}else if(nums[i] < 0) {
//交替就够了,很好的处理技巧
prod = -prod;
}
}
return prod;
}
阶乘0的个数
很多数学相关算法的关键在于找到怎么通过最简洁的方式来解决问题,而不是硬算。例如:面试题16.05:设计一个算法,算出 n 阶乘有多少个尾随零
。
这个题如果硬算,一定会超时,其实我们可以统计有多少个 0,实际上是统计 2 和 5 一起出现多少对,不过因为 2 出现的次数一定大于 5 出现的次数,因此我们只需要检查 5 出现的次数就好了,那么在统计过程中,我们只需要统计 5、10、15、 25、 … 5^n 这样 5 的整数倍项就好了,最后累加起来,就是多少个 0。代码就是:
/**
* 解题思路:
* 1、那么 n 过大时,从 1 遍历到 n, 那么会超时,因此我们修改下规律
*
* n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) ...
* 我们发现,
* 每隔 5 个数就会出现 一个 5,因此我们只需要通过 n / 5 来计算存在存在多少个 5 个数,那么就对应的存在多少个 5
* 但是,我们也会发现
* 每隔 25 个数会出现 一个 25, 而 25 存在 两个 5,我们上面只计算了 25 的一个 5,因此我们需要 n / 25 来计算存在多少个 25,加上它遗漏的 5
* 同时,我们还会发现
* 每隔 125 个数会出现一个 125,而 125 存在 三个 5,我们上面只计算了 125 的两个 5,因此我们需要 n / 125 来计算存在多少个 125,加上它遗漏的 5
* ...
*
* 因此我们 count = n / 5 + n / 25 + n / 125 + ...
* 最终分母可能过大溢出,上面的式子可以进行转换
*
* count = n / 5 + n / 5 / 5 + n / 5 / 5 / 5 + ...
* 因此,我们这样进行循环
* n /= 5;
* count += n;
* 这样,第一次加上的就是 每隔 5 个数的 5 的个数,第二次加上的就是 每隔 25 个数的 5 的个数 ...
* @param n n
* @return int
*/
public int trailingZeroes(int n) {
int cnt = 0;
for (long num = 5; n / num > 0; num *= 5) {
cnt += n / num;
}
return cnt;
}