【精選】JAVA算法題(二十二)

一、兩數之和

題目:

/**
 * 不使用運算符 + 和 - ​​​​​​​,計算兩整數 ​​​​​​​a 、b ​​​​​​​之和。
 *
 * 示例 1:
 * 輸入: a = 1, b = 2
 * 輸出: 3
 *
 * 示例 2:
 * 輸入: a = -2, b = 3
 * 輸出: 1
 */

這道題的本意其實就是在考你的位運算,如何利用位運算來進行數的加減

可以分爲兩步,就像我們小學做算數運算一樣,先把每位相加不考慮進位,然後再算一遍進位

總體思路:兩個整數a, b; a ^ b是無進位的相加; a&b得到每一位的進位;讓無進位相加的結果與進位不斷的異或, 直到進位爲0

public int getSum(int a, int b) {
        int sum, carry;
        sum = a ^ b;
        //異或這裏可看做是相加但是不顯現進位,比如5 ^ 3
         /*0 1 0 1
           0 0 1 1
         ------------
           0 1 1 0
        上面的如果看成傳統的加法,不就是1+1=2,進1得0,但是這裏沒有顯示進位出來,僅是相加,0+1或者是1+0都不用進位*/
        carry = (a & b) << 1;
        //相與爲了讓進位顯現出來,比如5 & 3
        /* 0 1 0 1
           0 0 1 1
         ------------
           0 0 0 1
        上面的最低位1和1相與得1,而在二進制加法中,這裏1+1也應該是要進位的,所以剛好吻合,但是這個進位1應該要再往前一位,所以左移一位*/
        if(carry != 0)  //經過上面這兩步,如果進位不等於0,那麼就是說還要把進位給加上去,所以用了尾遞歸,一直遞歸到進位是0。
        {
            return getSum(sum, carry);
        }
        return sum;
    }

把上述代碼整理濃縮

public int method2(int a, int b) {
    return b == 0 ? a : getSum(a^b,(a&b)<<1);
}

二、第一個不重複的字符

題目:

/**
 * 給定一個字符串,找到它的第一個不重複的字符,並返回它的索引。如果不存在,則返回 -1。
 *
 * 案例:
 * s = "leetcode"
 * 返回 0.
 * s = "loveleetcode",
 * 返回 2.
 * 注意事項:您可以假定該字符串只包含小寫字母。
 */

之前有做過類似的題,這些題的思路都很相近,統計出每個字符串中每個字母出現的次數,然後再根據題目做操作。最笨的方法是使用Map,效率高的方法便是使用字符轉換成ASCII碼當數組下標來存。

    public int firstUniqChar(String s) {
        int[] m = new int[26];
        for (int i = 0; i < s.length(); ++i)
            m[s.charAt(i)-'a']++;
        char[] chars=s.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (m[chars[i]-'a']==1){
                return i;
            }
        }
        return -1;
    }

三、第N個數字

題目:

/**
 * 在無限的整數序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 n 個數字。
 *
 * 注意:
 * n 是正數且在32爲整形範圍內 ( n < 231)。
 *
 * 示例 1:
 * 輸入:
 * 3
 * 輸出:
 * 3
 *
 * 示例 2:
 * 輸入:
 * 11
 * 輸出:
 * 0
 *
 * 說明:
 * 第11個數字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 裏是0,它是10的一部分。
 */

題目說的很模糊,一時間很難理解,大致的意思就是把N個數串在一起,比如10個數的組合就是“12345678910”,這樣的一個字符串,找到它的第十個數字就是1,第十一個就是0。

對於這種無限序列的或者數字很大的題目來說肯定是不可以用暴力的,肯定會超出時間限制的。那你就要想辦法找出一些數學規律來解題。我們從數字的位數來發現規律,一位數是1-9有9個,兩位是10-99有90個,三位數是100-999有900個,那麼規律找到了,我們可否利用它來解題呢?

給定一個n,先找所在的區間(如1-9,10-99...),然後求出它是所在的區間的第多少個數字,再算出是這個數字的第幾位。找區間按照公式n=n-9*1-90*2-900*3...-9*10^(i-1)*i, 求出的i指它在i位數的區間內,n爲該區間第n個數字,假設公式得到的n=4,i=2,那麼說明是兩位數,4/i=2,說明是第2個數字,即11,4%i=0,說明是第二個數字的最後一位,即1。 再假設共識得到n=25,i=3,說明是三位數,25/3=8,25%3=1,說明三位數中第8+1個數字,是這個數字的第1位,即是108中的1。

    public int method1(int n) {
        long len = 1, base = 1;
        long m = n;
        while (m > 9 * base * len) {
            m -= 9 * base * len;
            len++;
            base *= 10;
        }
        int curNum = (int) (base + (m - 1) / len);
        return ((curNum + "").charAt((int) ((m - 1) % len)) - '0');
    }

四、讀表

題目:

/**
 * 二進制手錶頂部有 4 個 LED 代表小時(0-11),底部的 6 個 LED 代表分鐘(0-59)。
 * 每個 LED 代表一個 0 或 1,最低位在右側。
 * 
 * 二進制手錶頂部有 4 個 LED 代表小時(0-11),底部的 6 個 LED 代表分鐘(0-59)。
 * 每個 LED 代表一個 0 或 1,最低位在右側。
 */

這個手錶不錯!和之前的一道題有異曲同工之妙,那道題是給定數字去組出時間,這道題只是拐了一個彎,但也爲我們提供了方便。

給定亮的燈數去找所有可能的時間,這裏要注意的問題就是邊界,12:00不行,0:60也不行,還要注意格式問題

我們只需要去遍歷所有可能的時間,共需要循環12*60,以計算機的速度來說很快,計算機很擅長這種重複的計算,然後挑出亮的等數是指定數量的就可以了,判斷的方式很多種,但最好使用位運算,因爲快。

一個重要的方法Integer.bitCount()該方法可以返回數值轉換爲二進制中1的數量。

    public static List<String> readBinaryWatch(int num) {
        List<String> ret = new ArrayList<String>();
        for(int h = 0;h<12;h++){
            for(int m = 0;m<60;m++){
                if(Integer.bitCount(h<<6|m)==num){
                    ret.add(h+(m<10?":0":":")+m);
                }
            }
        }
        return ret;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章