leetcode 326. Power of Three

来源:互联网 发布:保山有没有java培训班 编辑:程序博客网 时间:2024/05/22 02:04

Given an integer, write a function to determine if it is a power of three.

Follow up:
Could you do it without using any loop / recursion?

这道题要求很简单,就是判断n是否是3的乘方。难点在于不能用循环和递归。(注意:1是3的乘方,因为1是3的0次方。)

循环和递归的解法就不提了。这道题有这几种解法:

Method 1

算出Int最大值范围内的3的最大乘方数是:1162261467,判断n能不能整除它。

public boolean isPowerOfThree(int n) {    // 1162261467 is 3^19,  3^20 is bigger than int      return ( n>0 &&  1162261467%n==0);}

Method 2

如果 log10(n) / log10(3) 得到的是整数,那么n就是3的成分。 但是这里  需要注意的是, java的Math.log(x)方法默认的是自然数底数,即以e作为底数。但是这里不能够使用自然数底数,因为java在这里会有 round off error for n=243 ,更像是一个巧合。当 n=243, 我们得到的是下面的结果。

log(243) = 5.493061443340548    log(3) = 1.0986122886681098   ==> log(243)/log(3) = 4.999999999999999log10(243) = 2.385606273598312    log10(3) = 0.47712125471966244   ==> log10(243)/log10(3) = 5.0

这是因为 log(3) 实际上轻微地比它真正的值要大,导致了 求比结果 比 真正的值更小。

public boolean isPowerOfThree(int n) {    return (Math.log10(n) / Math.log10(3)) % 1 == 0;}

如果害怕出现这种round off error的巧合出错,可以用下面两种方案来弥补:

public boolean isPowerOfThree(int n) {    return n==0 ? false : n==Math.pow(3, Math.round(Math.log(n) / Math.log(3)));}

或者

public boolean isPowerOfThree(int n) {    return n>0 && Math.abs(Math.log10(n)/Math.log10(3)-Math.ceil(Math.log10(n)/Math.log10(3))) < Double.MIN_VALUE;}
Method 3 Cheating Method

cheating method其实并不是一个好方法,但是对于这样的 乘方 问题,如果我们需要检查很多次,那么先把所有可能值存到hashset中也不失为一种好办法。 

public boolean isPowerOfThree(int n) {    HashSet<Integer> set = new HashSet<>(Arrays.asList(1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, 129140163, 387420489, 1162261467));    return set.contains(n);}

Method 4

思路是把 n 转化为三进制的表示,然后检查 该三进制字符串 是否呈现 100.....的格式(其中0的个数>=0)。format 10* where 0* means k zeros with k>=0.

public boolean isPowerOfThree(int n) {    return Integer.toString(n, 3).matches("10*");}

这道题有solutions:https://leetcode.com/problems/power-of-three/solution/

Solution


Approach #1 Loop Iteration [Accepted]

Java

public class Solution {    public boolean isPowerOfThree(int n) {        if (n < 1) {            return false;        }        while (n % 3 == 0) {            n /= 3;        }        return n == 1;    }}

Complexity Analysis

  • Time complexity : O(log_b(n))O(logb(n)). In our case that is O(log_3n)O(log3n). The number of divisions is given by that logarithm.

  • Space complexity : O(1)O(1). We are not using any additional memory.


Approach #2 Base Conversion 进制转换 [Accepted]

在十进制中,所有10的乘方的数字 都以 1 开头,之后跟着 (比如 10, 100, 1000)。 这对于其他进制和对应的乘方也适用。所以我们可以把 n 转化为三进制的表达形式,如果它的格式是100...0, 那么它就是3的乘方。

使用以下的正则表达式来判断:字符串是否以 1 开头 ^1, 后面跟着 0个或者更多个数字 0 0* ,并且字符串中不包含其他的东西 $.

Java

public class Solution {    public boolean isPowerOfThree(int n) {        return Integer.toString(n, 3).matches("^10*$");    }}

Complexity Analysis

  • Time complexity : O(log_3n)O(log3n).

    Assumptions:

    • Integer.toString() - Base conversion is generally implemented as a repeated division. The complexity of should be similar to our approach #1: O(log_3n)O(log3n).
    • String.matches() - Method iterates over the entire string. The number of digits in the base 3 representation of n is O(log_3n)O(log3n).
  • Space complexity : O(log_3n)O(log3n).

    We are using two additional variables,

    • The string of the base 3 representation of the number (size log_3nlog3n)
    • The string of the regular expression (constant size)

Approach #3 Mathematics [Accepted]

log方法:

n=3ii=log3(n)i=logb(n)logb(3)

只有当 i 是整数时,n 才是3的乘方。判断一个数是不是整数,我们可以用 % 1 来取出小数部分,检查小数部分是否是0。

Java

public class Solution {    public boolean isPowerOfThree(int n) {        return (Math.log10(n) / Math.log10(3)) % 1 == 0;    }}

Attention

当我们开始使用 double时,就有可能产生精度错误,这种解法就有问题。这意味着当比较 double数字时,我们不能使用 == 。因为 Math.log10(n) / Math.log10(3) 的结果可能是 5.0000001 或者4.9999999。为了解决这个问题,我们可以使用 epsilon来避免这个问题。令final double epsilon=0.00000001;(epsilon通常表示表示大于零的最小正Double值。这里可以自己定义,或者直接令epsilon=Double.MIN_VALUE。)

需要注意的是,Integer.MIN_VALUE自不必说,就是32位整型能存储的最小数字:0x80000000,是一个负数。但是Double.MIN_VALUE却是一个正数,Double.MIN_VALUE表示的是64位双精度值能表示的最小正数。如果需要用到Double的负无穷,可以用Double.NEGATIVE_INFINITY。

Java

return (Math.log(n) / Math.log(3) + epsilon) % 1 <= 2 * epsilon;

Complexity Analysis

  • Time complexity : UnknownUnknown The expensive operation here is Math.log, which upper bounds the time complexity of our algorithm. The implementation is dependent on the language we are using and the compiler[3]

  • Space complexity : O(1)O(1). We are not using any additional memory. The epsilon variable can be inlined.


Approach #4 Integer Limitations [Accepted]

3^{\lfloor{}log_3{MaxInt}\rfloor{}} = 3^{\lfloor{}19.56\rfloor{}} = 3^{19} = 11622614673log3MaxInt=319.56=319=1162261467


Java

public class Solution {    public boolean isPowerOfThree(int n) {        return n > 0 && 1162261467 % n == 0;    }}

Complexity Analysis

  • Time complexity : O(1)O(1). We are only doing one operation.

  • Space complexity : O(1)O(1). We are not using any additional memory.

Performance Measurements

Iterations10^610610^710710^810810^9109MaxintMaxintJava Approach #1 (Naive)0.040.070.302.475.26Java Approach #2 (Strings)0.684.0238.90409.16893.89Java Approach #3 (Logarithms)0.090.504.5945.5397.50Java Approach #4 (Fast)0.040.060.080.410.78

References

  • [1] https://en.wikipedia.org/wiki/Fast_inverse_square_root
  • [2] https://en.wikipedia.org/wiki/Regular_expression
  • [3] http://developer.classpath.org/doc/java/lang/StrictMath-source.html
  • [4] http://www.cut-the-knot.org/recurrence/conversion.shtml
  • [5] http://www.cnblogs.com/liujinhong/p/6432107.html Java输出double类型中的最小正数和最大正数。


原创粉丝点击