《編程之美》之前有看過,不過看完之后不僅啥也沒記住,反而是把自己繞得一團暈,重讀《編程之美》也是想重新梳理一下算法中的邏輯,並試圖找出那些所謂“美”的算法的共性,同時也希望能夠結交一些有着共同愛好的童鞋。好了,廢話到此,咱們開始吧。
1、題目:對於一個字節(8bit)的變量,求其二進制表示中 “1” 的個數,要求算法的執行效率盡可能高。
解法一、
思路:(輸入)變量為125,其二進制表示為01111101,統計該二進制表示中“1”出現的個數,可(從低位到高位)依次統計每位上“1”出現情況。
計算:
1、計算(輸入)變量二進制表示中最低位數字(num % 2),若為“1”則count加1;
2、計算(輸入)變量二進制表示向右位移一位值(num / 2);
3、迭代步驟1、步驟2,直到(輸入)變量值為0;
4、返回count值。
代碼:
public int countNumberOne(int inputNumber) { int retCount = 0; while(inputNumber != 0) { if(1 == (inputNumber % 2)) { ++retCount; } inputNumber /= 2; } return retCount; }
注:該算法時間復雜度為O(N),N為輸入變量的二進制表示長度。
解法二、
思路:解法二是對解法一的改進,對於(輸入)變量只需統計“1”的出現次數即可。我們知道num & num – 1的二進制運算,是對num最低“1”位計算為“0”,特別地,若num & num – 1 = 0,則表示num為2的冪次方。因此,可直接依次(從低位到高位)統計最低“1”位個數。
計算:
1、計算(輸入)變量二進制表示是否為0,若不為0,則count加1;
2、計算(輸入)變量二進制表示最低“1”位值變為“0”(num &= num –1);
3、迭代步驟1、步驟2,直到(輸入)變量值為0;
4、返回count值。
代碼:
public int countNumberOne2(int inputNumber) { int retCount = 0; while(inputNumber != 0) { retCount++; inputNumber &= (inputNumber - 1); } return retCount; }
注:該算法時間復雜度為O(M),M為輸入變量的二進制表示中“1”的個數。
解法三、
思路:因為題目要求輸入變量長度為8bit,即一個字節,其取值范圍為0~255。因此,可直接建立0~255中每個數字所對應“1”出現個數的關系映射表,鍵值(key–value)關系可簡單通過數組來表示:數組下標(i)表示關鍵字(key),即所要查找的數字;數組值(arr[i])表示所要查找的結果,即“1”出現個數。
計算:
1、裝載關系映射表(即0~255中每個數字所對應“1”個數的映射關系);
2、以輸入數字為鍵值(key),查找關系映射表中該鍵值對應的value值,並返回。
代碼:
static int[] countTable = null; static void loadCountTable() { countTable = new int[256]; for(int i = 0; i < 256; i++) { countTable[i] = countNumberOne(i); } } public int countNumberOne3(int inputNumber) { if(countTable == null) { loadCountTable(); } return countTable[inputNumber]; }
注:該算法時間復雜度為O(1),空間復雜度為O(2 ^ n),n為輸入變量最大長度。
另:該算法為典型的空間換時間案例,得益於存儲開銷遠低於計算(cpu)開銷,空間換時間在計算機領域中十分常見,如:搜索引擎中的倒排索引、網站中的降級處理(如某電子商務公司在價格戰中會將部分動態頁面靜態化,以提高響應時間)等。如果大家有什么這方面的案例,希望能共同探討,謝謝!