#191. Number of 1 Bits
題目描述
寫一個函式處理無符號整數(unsigned integer),然後回傳位元為'1'的數量。
- 在某些程式語言中,像是JAVA。沒有無符號整數的型別。在這種情況下,輸入會是有符號的整數型別。但是這不應該影響到整個函式的實作結果,不論有無符號,整數的二元表示都是一樣的。
- 在JAVA中,符號整數使用二進制補碼。
限制條件
- 輸入是長度為32的用來表示二進制數值的字串。
解題思路
限制條件是Test Case的輸入條件,但是函式的實際輸入為整數,因此不用在函式內處理字串。
Intuition
在十進制中,整數的偶數在二進位中的尾數都會是0
,而奇數會是1
。 因此只要使用模除來判定尾數是否為1
,並且搭配位元右移來把左邊的位元往右移,就可以不斷的判斷位元值直到全部為0為止。
public class Solution {
public int HammingWeight(uint n) {
int c = 0;
while(n!=0){
if(n%2==1) c++;
n>>=1;
}
return c;
}
}
複雜度分析:
- 時間複雜度:由於是32位元,while迴圈最多執行32次就結束了,因此時間複雜度為O(1)。
- 空間複雜度:空間複雜度為O(1)。
Follow-up
如果這個函式會被呼叫很多次,你要怎麼去改善它?
Optimized
如果會被呼叫很多次,每一次呼叫都會計算肯定是比較花費時間的,因此要建立快取讓答案可以快速被取得。 參考338.Counting Bits的方法,透過取得之前計算的結果來快速計算新的答案。 我們將32位元分成兩個16位元,先計算前16位元每個值的bit數量,然後存入快取之中,超過16位元的部分會是16位元內的數值的組合。
Solution的建構式在Leetcode中是受保護的,因此沒辦法在Leetcode中測試C#。
public class Solution {
int len=0;
int[] pre_ans;
Solution(){
len = 1<<16;
pre_ans = new int[len];
PreCompute();
}
public int PreCompute(){
for(int i=0;i<len;i++){
pre_ans[i] = pre_ans[i>>1]+ i%2;
}
return 0;
}
public int HammingWeight(uint n) {
return pre_ans[n/len] + pre_ans[n%len];
}
}
複雜度分析:
- 時間複雜度:時間複雜度為O(1),但在大量呼叫的情況下,排除重新建立實例再呼叫的行為,執行的速度會快於每一次呼叫都進行計算。
- 空間複雜度:事先建立了一部分空間用來存放快取,但在執行時不會增加使用空間,因此空間複雜度為O(1)。
學習重點
- 位元操作
- 分治法(Divide And Conquer)