演算法學習之二進位制的妙用

SHISME發表於2019-02-07

有一個笑話,世界上有10種人,一種是看得懂二進位制的,一種是看不懂的。 如果你看懂了這個笑話,這篇文章就是適合你讀的了

Single Number

leetcode 上有一道這樣的題,Single Number,題目是要你要找到陣列中唯一隻存在一個的數字,其他數字都出現兩次。這道題目非常的簡單,我們可以用 hash 表來記錄所有數字的次數,然後找到次數為1的那個數字。如果用二進位制來解決這道題效率會快很多。

二進位制的解法

二進位制中有一個操作符叫做位異或,他的作用是兩個位數字相同則為0,不同則為1,即 1^1=0,0^0=0,1^0=1,0^1=1;

通過這個運算子的特點我們可以知道,任何一個數對自己位異或操作,得到的結果都是 00000000,00000000對任意數字位異或得到的都是那個數字。並且 A ^ B ^ C = C ^ B ^A,這個操作符是滿足交換律的。下面看一下 js 的簡單解法:


/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    return nums.reduce((res, cur) => res ^ cur);
};

複製程式碼

毒藥問題

有 8 個一模一樣的瓶子,其中有 7 瓶是普通的水,有一瓶是毒藥。任何喝下毒藥的生物都會在一星期之後死亡。現在,你只有 3 只小白鼠和一星期的時間,如何檢驗出哪個瓶子裡有毒藥?

解決思路

我們用二進位制給每瓶水進行編號,編號分別為,000,001,010,011,100,101,110,111,分別對應1-8的瓶子,然後讓第一隻老鼠喝第一位為1的,第二隻老鼠喝第二位為1的,第三隻老鼠喝第三位為1的,假設第四瓶水有毒,即011有毒

  • 第一隻老鼠喝了 100,101,110,111,結果:沒死,記作0
  • 第二隻老鼠喝了 010,011,110,111,結果:死了,記作1
  • 第三隻老鼠喝了 001,011,101,111,結果:死了,記作1

根據死亡結果,剛好是第四瓶水011,這只是一個巧合嗎,恐怕不是的,我們可以用數學的思維來證明一下這個問題

證明

每隻老鼠喝了毒藥只會出現兩種情況死或者不死,一隻老鼠可以驗證兩瓶藥有沒毒,即2^1,兩隻老鼠可以驗證2^2瓶藥,三隻老鼠可以驗證2^3瓶藥,那麼要怎麼去驗藥呢

  • 我們讓每隻老鼠喝某一位為1的的所有藥,如果那隻老鼠死了則說明毒藥的某一位編號為1,比方說第一隻老鼠喝了所有第一位為1的毒藥死了,則說明毒藥的第一位編號為1,如果沒死,則說明毒藥的那一位編號為0
  • 這裡如果三隻老鼠都沒死,則說明毒藥的三位編號都為0,剛好是三隻老鼠都每沒喝的第一瓶。

擴充問題

有 1000 個一模一樣的瓶子,其中有 999 瓶是普通的水,有一瓶是毒藥。任何喝下毒藥的生物都會在一星期之後死亡。現在,你只有 10 只小白鼠和一星期的時間,如何檢驗出哪個瓶子裡有毒藥?

根據上面的問題,我們能夠知道 2 ^ 10 = 1024 > 1000,也是通過對每個瓶子進行二進位制編號即可檢驗出哪個瓶子有毒。

問題升級

現在,有意思的問題來了:如果你有兩個星期的時間,為了從 1000 個瓶子中找出毒藥,你最少需要幾隻老鼠?注意,在第一輪實驗中死掉的老鼠,就無法繼續參與第二次實驗了。

擴充問題思路

我們要達到的目的是用盡可能少的老鼠,在兩週之內找到結果,所以我們必須要進行兩輪的實驗,那麼每隻老鼠可能就會出現三種情況,第一輪死掉,第二輪死掉,第二輪活著,上面一題老鼠會出現兩種情況用的是二進位制,那麼這一題很明顯我們需要用到三進位制。3 ^ 6 = 729, 3 ^ 7 = 2187, 很明我們至少是需要7只老鼠。

如何喂藥

  • 還是和前面一樣,第一輪的時候,我們讓每隻老鼠喝某一位為編號為2的藥,如果某隻老鼠死了,則說明毒藥的那一位編號為2,如果老鼠全死了,我們連第二輪都不用了,直接可以確定毒藥的編號為2222222。
  • 第二輪如果還剩多少隻老鼠,則說明毒藥有多少位的編號為0或者1,我們剩k只老鼠,k位二進位制數需要確認,因為那幾位數已經排除了是2的可能性。則由回到了上一題,我們繼續用上一輪的喂法即可。

稱重問題

27個小球。其中一個比其他小球都要重一點。給你一個天平,最多稱3次,找出這個特殊的小球。

思路

這題也是需要用到三進位制的思路來解決的,我們每次稱可能出現三種狀態左邊重,右邊重,一樣重,3 ^ 3 剛好27,所以我們是可以在3次內找到這個小球。

如何稱

先給每個球編號000,001,002,010,...,222

  • 第一次稱第一位為2的和第一位為1的所有小球,他們同樣都是9個,哪邊重則說明,較重的小球的第1為幾,如果一樣重則說名第一位為0
  • 第二次稱第二位為2的和第二位為1的所有小球,他們也同樣都是9個,哪邊重則說明,較重的小球的第2位為幾,如果一樣重則說名第二位為0
  • 第三次稱第三位為2的和第三位為1的所有小球,他們也同樣都是9個,哪邊重則說明,較重的小球的第3位為幾,如果一樣重則說名第三位為0

經過三次稱重我們就可以找到較重的哪一個小球了。

總結

面對這種問題,其實解決思路都大通小異,都是需要找到問題關鍵狀態,通過關鍵狀態的數量我們可以知道需要用到多少進位制來解決問題。然後根據題目制定方案,來確定每一位的狀態碼最終得到結果。

相關文章