天天看點

【算法】解題總結:劍指Offer 11 二進制中1的個數(4 種方法)、劍指Offer 12 數值的整數次方

JZ11 二進制中1的個數

(中等)

題目

描述

輸入一個整數,輸出該數32位二進制表示中1的個數。其中負數用補碼表示。

示例

輸入:

10

傳回值:

2

解題過程

1 純正數版——方法一

此題我首先是按純正數的情況來進行考慮的,但因為負數涉及補碼,是以這種方式很對負數是行不通的,但是我還是想練習一下針對這種問題的編碼能力,而且如果隻是純正數(當然,0 的情況特殊可考慮,可不考慮),也可以單獨出一道題,也有研究的價值。

對一個十進制正數轉為二進制的形式,簡單來說就是拆成了多個 2^n 的組合(當然,需是先從高位算起的個數最少的組合),在二進制的相應位中用 有效數1 進行标記,是以,我們可以找出數 n 的最高二進制,同時讓 n 減去這個數得到新的 n,再對新的 n 進行重複操作,過程中記錄下 n 執行減法的次數即可得知 n 的二進制形式中有幾個 1 。

【實作】

public class Solution {
    public int NumberOf1(int n) {
        int num = 0; // 二進制形式中 1 的個數

        while (n > 0) {
            int i = 2;
            while (i < n) {
                i *= 2;
            }
            if (i != 2 && i != n) { //當 while 循環體執行過,并且 i 多乘了一次 2 時
                i /= 2;
            }
            n -= i;
            num++;
        }
        return num;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println("solution.NumberOf1(10) = " + solution.NumberOf1(10));
        System.out.println("solution.NumberOf1(8) = " + solution.NumberOf1(8));
        System.out.println("solution.NumberOf1(3) = " + solution.NumberOf1(3));
        System.out.println("solution.NumberOf1(1) = " + solution.NumberOf1(1));
    }
}      
【算法】解題總結:劍指Offer 11 二進制中1的個數(4 種方法)、劍指Offer 12 數值的整數次方

2 純正數版——方法二

或是用另一種更簡潔的方式實作,參數 n 對 2 求餘,如果沒有餘數,則可能是正好的 2 的整數倍,也可能不是,是以隻要 n 不等于 0 ,再對 n 進行除以 2 ,在這其中如果出現餘數為 1 的情況,則說明目前的 n 值,用 2 進制表示時,1 的數量肯定是多餘 1 個的,是以要計數上這種情況的一次,并且,之後 n 因為是整除 2 ,是以最後肯定還會出現 1 次求餘為 1 的情況,這也正是目前 n 值(當然不是說初始的 n 值)對應的目前最高位的二進制 1,是以,用這種方法,最終也可以求出正數情況時的二進制中 1 的個數。

【實作】

public class Solution {
    public int NumberOf1(int n) {
       int ans = 0;
        while (n != 0) {
            int tmp = n % 2;
            if (tmp == 1) ++ans;
            n /= 2;
        }
        return ans;
    }
}      

3 此題正式版——二進制位移法

直接将整數看成二進制,然後采用與運算、移位的方法。

首先,我們先寫一下正數情況下的實作:

【實作】

public class Solution {
    public int NumberOf1(int n) {
        int ans = 0;
        while (n != 0) {
            if ((n & 1) == 1)  ans++;
            n >>= 1;
        }
        return ans;
    }
}      

由于正數情況,右移 1 位,左位是補 0 ,是以,可以終止 while 循環,最終得出正确答案,但是如果我們想處理負數的情況,即左位補 1 ,在上述代碼中就解決不了,而且 while 就終止不了,況且一直左移隻會讓 1 的個數越來越多。

是以,我們換種方式,我們用 1 作為移動方,使用初始的 1 不斷左移,并與參數 n 進行與運算,這樣最終這個初始的 1 是可以将 1 移出左邊界,進而變成 0 ,并且也已經将參數 n 中的 1 全部找出,是以,是可行的,代碼如下:

【實作】

public class Solution {
    public int NumberOf1(int n) {
        int ans = 0;
        int flag = 1;
        while (flag != 0) {
            if ((n & flag) != 0)  ans++;
            flag <<= 1;
        }
        return ans;
    }
}      

這個算法可以解決此題,但是需要運作32次(int 型為 4 個位元組,是以在記憶體中占 32 位,并且是将 1 從右向左移出)。

【算法】解題總結:劍指Offer 11 二進制中1的個數(4 種方法)、劍指Offer 12 數值的整數次方

4 此題正式版——技巧法

十分巧妙且高效,題解也解釋的十厘清楚,我不再過多贅述,隻能說發明這個算法的人太強了,并且是十分善于觀察和探索的人才能想出來,當然我們也不能排除是偶然的靈感迸發。

【算法】解題總結:劍指Offer 11 二進制中1的個數(4 種方法)、劍指Offer 12 數值的整數次方

JZ12 數值的整數次方

(中等)

題目

描述

給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。

保證base和exponent不同時為0。不得使用庫函數,同時不需要考慮大數問題,也不用考慮小數點後面0的位數。

示例1

輸入:

2.00000,3

傳回值:

8.00000

示例2

輸入:

2.10000,3

傳回值:

9.26100

示例3

輸入:

2.00000,-2

傳回值:

0.25000

說明:

2的-2次方等于1/4=0.25

思路

此題根據指數的情況來劃分即可。

當指數為 0 時,若底數也為 0 ,因 0 的 0 次幂無意義,是以此情況應提示錯誤資訊;

當指數為正數時,将指數個底數相乘傳回結果即可;

當指數為負數時,應将底數轉為其倒數,并且,若在循環計算時借助指數與目前下标大小作為循環終止條件,則指數應提前轉為其相反數。

實作

public class JZ12數值的整數次方 {
    public double Power(double base, int exponent) {
        if (exponent == 0) {
            if (base == 0) {
                try {
                    throw new Exception("0的0次方無意義");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return 1;
        } else if (exponent > 0) { //指數為正數
            if (base == 0) {
                return 0;
            }
        } else if (exponent < 0) { //指數為負數
            if (base == 0) {
                try {
                    throw new Exception("當指數為負數,底數為0時無意義");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            base = 1 / base;
            exponent = -exponent;
        }
        double result = 1;
        for (int i = 0; i < exponent; i++) {
            result *= base;
        }
        return result;
    }


    public static void main(String[] args) {
        JZ12數值的整數次方 s = new JZ12數值的整數次方();
        System.out.println("s.Power(2.00000, 3) = " + s.Power(2.00000, 3));
        System.out.println("s.Power(2.00000, 0) = " + s.Power(2.00000, 0));
        System.out.println("s.Power(2.00000, -2) = " + s.Power(2.00000, -2));
    }
}      

繼續閱讀