LeetCode上做题之体会(三)

1、判断一个数能不能被4开方

Power of Four
Given an integer (signed 32 bits), write a function to check whether it is a power of 4.
Example:
Given num = 16, return true. Given num = 5, return false.
Follow up:
Could you solve it without loops/recursion?

要是题目没有那局限制的话相信很多人一看到题目都会直接使用类方法(库函数)或者用循环/递归来解决这道题的吧。要是以前的我的话也会是这样做的,但是做这种类型的题目多了之后就会发现,自己不会老是喜欢用一种方法来解决一种类型的问题。

以下是我的想法:

    public boolean isPowerOfFour(int num) {
        if(num == 1) return true;
        if((num % 10 == 4 || num % 10 == 6) && Integer.bitCount(num) == 1 && num > 0)
            return true;
        else
            return false;
    }

唉!我还是没能想到下面这种用二进制数操作来解决问题!)下面确实是一个更快,效率更高的代码:

    public boolean isPowerOfFour(int num) {
        return num > 0 && (num&(num-1)) == 0 && (num & 0x55555555) != 0;
    }
解释一下上面为何还要与一下0x55555555,由于num & (num - 1)只保证了二进制位中只出现一个1,但是没有保证这个1会出现在奇数位,这个跟我用的bitCount()函数其实是一样的意思。有了最后num & 0x55555555就保证了这个这个1一定会出现奇数位上。

2、以集合式形式输出杨辉三角

Pascal’s Triangle
Given numRows, generate the first numRows of Pascal’s triangle.
For example, given numRows = 5,
Return
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]

//简洁的代码:
    public List<List<Integer>> generate(int numRows){
        List<List<Integer>> a = new ArrayList<List<Integer>>();
        ArrayList<Integer> b = new ArrayList<>();
        for(int i = 0; i < numRows; i++){
            b.add(0,1);
            for(int j = 1; j < b.size() - 1; j++){
                b.set(j,b.get(j) + b.get(j+1));
            }
            a.add(new ArrayList(b));
        }
        return a;
    }
//比较长的代码:
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> finalLs = new ArrayList  <List<Integer>> ();
        if(numRows==0)
            return finalLs;
        List<Integer> ls;
        if(numRows>0) {
            ls = new ArrayList<Integer>();
            ls.add(1);
            finalLs.add(ls);
        }
        if(numRows>1) {
            ls= new ArrayList<Integer>();
            ls.add(1);
            ls.add(1);      
            finalLs.add(ls);
        }
        int j=2;
        if(numRows>2){
            while(numRows>j) {   
                ls= new ArrayList<Integer>();
                ls.add(1);
                List <Integer> lsprev= finalLs.get(j-1);
                for(int i = 1; i < lsprev.size(); i+=1)
                    ls.add(lsprev.get(i-1)+lsprev.get(i));
                ls.add(1);
                finalLs.add(ls);
                j++;
            }
        }
        return finalLs;
    }

以上两端代码中,简洁的代码在提交的时候运行时间是2ms,后者在提交之后的运行时间是1ms,我认为后者其实是跟前者是一样的意思和思路,只是多创建了几个中间集合而已。而这里面的运行时间问题,我最后发现,原来这道题LeetCode上的测试用例只有15个,要是多的话,应该还是差不多的时间。对于我来说,还是更倾向于用前者。

3、数素数

Count Prime
Count the number of prime numbers less than a non-negative number, n.

又是一道题目很简单的题目。最简单的方法,以前刚学习语言的时候,会遇到判断一个数是否为素数的问题。在这个问题中要是用那个方法的话,时间复杂度就为O(n^2)。
能不能优化一下呢?
其实很容易我们就会想到,由于每个因子的取值都是小于√n的,所以被把循环边界从n变为√n之后我们确实能把复杂度降下来,但是降得还是不明显,最后时间复杂度还是O(n^1.5)这样的时间复杂度相对来说是小了点。
但是能不能再优化一下呢?
其实还有一种方法可以优化这个问题:筛子法Sieve of Eratosthenes
这是一种最有效的方法之一,下面有个演示图,根据这个图看的话,比较容易理解这个方法。
算法规模为121,此图来自于维基百科
首先,将2的倍数标记,之后再将3的倍数标记,再接着是5、7、11、13、……,其实就是一个递归的方式,每一次找到一个素数之后这个素数的倍数将不再是素数了。就把他们标记掉。这其实是一个用空间来换时间的一个做法。该算法的空间复杂度为O(n),时间复杂度为O(nlog log n)。

出题人贴的代码:
public int countPrimes(int n) {
   boolean[] isPrime = new boolean[n];
   for (int i = 2; i < n; i++) {
      isPrime[i] = true;
   }
   for (int i = 2; i * i < n; i++) {
      if (!isPrime[i]) continue;
      for (int j = i * i; j < n; j += i) {
         isPrime[j] = false;
      }
   }
   int count = 0;
   for (int i = 2; i < n; i++) {
      if (isPrime[i]) count++;
   }
   return count;
}
简洁的代码实现:
    public int countPrimes(int n) {
        boolean[] notPrime = new boolean[n];
        int count = 0;
        for (int i = 2; i < n; i++) {
            if (notPrime[i] == false) {
                count++;
                for (int j = 2; i*j < n; j++) {
                    notPrime[i*j] = true;
                }
            }
        }
        return count;
    }

(对了,上面边界判定没有用根号是为了避免重复使用“高耗”函数sqrt();)

4、整型断数(也许从这个名字上很难理解这道题是讲什么的)

Integer Break
Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.
For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
Note: you may assume that n is not less than 2.

它给的两个提示是这样的:
1、There is a simple O(n) solution to this problem.
2、You may check the breaking results of n ranging from 7 to 10 to discover the regularities.

对比前面几个我们就会发现规律4(2+2),5(2+3),6(3+3),7(2+2+3),8(2+3+3),9(3+3+3),10(3+3+4)。仔细观察就会发现,其实就会发现,从7之后每一个数就是前第三个数多一个数字3,这样规律是算找到了
以下是我的代码:

    public int integerbreak(int n){
        if(n <= 2) return 1;
        if(n == 3) return 2;
        if(n == 4) return 4;
        int[] num = new int[n+1];
        num[2] = 2;
        num[3] = 3;
        num[4] = 4;
        for(int i = 5; i <= n; i++){
            num[i] = 3 * num[i-3];
        }
        return num[n];
    }

下面这个是按照另外一种思路来的代码经过我的测试,下面这段代码很有可能就是LeetCode用来做参考的代码(因为我试了一个结果超出整型范围的数,结果我上面的代码和答案不一样):

    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        dp[1] = 1;
        for(int i = 2; i <= n; i++) {
            for(int j = 1; 2*j <= i; j++) {
                dp[i] = Math.max(dp[i], (Math.max(j,dp[j])) * (Math.max(i - j, dp[i - j])));
            }
        }
        return dp[n];
    }

这里的思路是这样的,举个例子,6=2+2+2=3+3,这里面就会比较2*2*2和3*3哪个比较大。这样到后面超出整型范围的,还是超不过那最后一个最大的数(2147445847)。

5、翻转字符串中指定部分

Reverse Vowels of a String
Write a function that takes a string as input and reverse only the vowels of a string.
Example 1:
Given s = “hello”, return “holle”.
Example 2:
Given s = “leetcode”, return “leotcede”.

首先,这道题上来就和之前做过的一道反转题目有点相像,但是又有点不同,不知为何,我首先想到的是用另外的空间直接将元音字母提取出来,在将他们反转之后一个一个地塞回去。我在提交之后再去看别人写的代码,才想到其实直接在字符串上面反转就ok了。以下是上面两种方法的实现,其实,第二种虽然有三个循环,但是时间复杂度是小于第一种的。

这个是我的方法实现(87ms):

public class Solution {
    static final String vowels = "aeiouAEIOU";

    public String reverseVowels(String s) {
        StringBuilder anos = new StringBuilder();
        char[] array = s.toCharArray();
        for(int i = 0; i < s.length(); i++){
            if(vowels.indexOf(array[i]) != -1){
                anos.append(array[i]);
            }
        }
        anos.reverse();
        for(int i = 0; i < s.length(); i++){
            if(vowels.indexOf(array[i]) != -1){
                array[i] = anos.charAt(0);
                anos.deleteCharAt(0);
            }
        }
        return new String(array); 
    }
}

别人的方法(6ms):

public class Solution {
    static final String vowels = "aeiouAEIOU";

    public String reverseVowels(String s) {
        int first = 0, last = s.length() - 1;
        char[] array = s.toCharArray();
        while(first < last){
            while(first < last && vowels.indexOf(array[first]) == -1){
                first++;
            }
            while(first < last && vowels.indexOf(array[last]) == -1){
                last--;
            }
            char temp = array[first];
            array[first] = array[last];
            array[last] = temp;
            first++;
            last--;
        }
        return new String(array);
    }
}
本系列未完,下一篇请看:LeetCode上做题之体会(四)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章