|
作者:帥地 來源丨苦逼的碼農(nóng) 位算法的效率有多快我就不說,不信你可以去用 10 億個數(shù)據(jù)模擬一下,今天給大家講一講位運(yùn)算的一些經(jīng)典例子。不過,最重要的不是看懂了這些例子就好,而是要在以后多去運(yùn)用位運(yùn)算這些技巧,當(dāng)然,采用位運(yùn)算,也是可以裝逼的,不信,你往下看。我會從最簡單的講起,一道比一道難度遞增,不過居然是講技巧,那么也不會太難,相信你分分鐘看懂。 判斷奇偶數(shù)判斷一個數(shù)是基于還是偶數(shù),相信很多人都做過,一般的做法的代碼如下 if( n % 2) == 01
有人可能會說,我們寫成 n % 2 的形式,編譯器也會自動幫我們優(yōu)化成位運(yùn)算啊,這個確實,有些編譯器確實會自動幫我們優(yōu)化。但是,我們自己能夠采用位運(yùn)算的形式寫出來,當(dāng)然更好了。別人看到你的代碼,我靠,牛逼啊。無形中還能裝下逼,是不是。當(dāng)然,時間效率也快很多,不信你去測試測試。 2、交換兩個數(shù)交換兩個數(shù)相信很多人天天寫過,我也相信你每次都會使用一個額外來變量來輔助交換,例如,我們要交換 x 與 y 值,傳統(tǒng)代碼如下: int tmp = x;這樣寫有問題嗎?沒問題,通俗易懂,萬一哪天有人要為難你,不允許你使用額外的輔助變量來完成交換呢?你還別說,有人面試確實被問過,這個時候,位運(yùn)算大法就來了。代碼如下: 我靠,牛逼!三個都是 x ^ y,就莫名交換成功了。在此我解釋下吧,我們知道,兩個相同的數(shù)異或之后結(jié)果會等于 0,即 n ^ n = 0。并且任何數(shù)與 0 異或等于它本身,即 n ^ 0 = n。所以,解釋如下: 把(1)中的 x 帶入 (2)中的 x,有 y = x^y = (x^y)^y = x^(y^y) = x^0 = x。 x 的值成功賦給了 y。 對于(3),推導(dǎo)如下: x = x^y = (x^y)^x = (x^x)^y = 0^y = y。
以后你要是別人看不懂你的代碼,逼格裝高點,就可以在代碼里面采用這樣的公式來交換兩個變量的值了,被打了不要找我。 講這個呢,是想告訴你位運(yùn)算的強(qiáng)大,讓你以后能夠更多著去利用位運(yùn)算去解決一些問題,一時之間學(xué)不會也沒事,看多了就學(xué)會了,不信?繼續(xù)往下看,下面的這幾道題,也是非常常見的,可能你之前也都做過。 3、找出沒有重復(fù)的數(shù)
這道題可能很多人會用一個哈希表來存儲,每次存儲的時候,記錄 某個數(shù)出現(xiàn)的次數(shù),最后再遍歷哈希表,看看哪個數(shù)只出現(xiàn)了一次。這種方法的時間復(fù)雜度為 O(n),空間復(fù)雜度也為 O(n)了。 然而我想告訴你的是,采用位運(yùn)算來做,絕對高逼格! 我們剛才說過,兩個相同的數(shù)異或的結(jié)果是 0,一個數(shù)和 0 異或的結(jié)果是它本身,所以我們把這一組整型全部異或一下,例如這組數(shù)據(jù)是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出現(xiàn)了一次,其他都出現(xiàn)了兩次,把他們?nèi)慨惢蛞幌拢Y(jié)果如下: 由于異或支持交換律和結(jié)合律,所以: 1^2^3^4^5^1^2^3^4 = (1^1)^(2^2)^(3^3)^(4^4)^5= 0^0^0^0^5 = 5。 也就是說,那些出現(xiàn)了兩次的數(shù)異或之后會變成0,那個出現(xiàn)一次的數(shù),和 0 異或之后就等于它本身。就問這個解法牛不牛逼?所以代碼如下 int find(int[] arr){時間復(fù)雜度為 O(n),空間復(fù)雜度為 O(1),而且看起來很牛逼。 4、2的n次方如果讓你求解 2 的 n 次方,并且不能使用系統(tǒng)自帶的 pow 函數(shù),你會怎么做呢?這還不簡單,連續(xù)讓 n 個 2 相乘就行了,代碼如下: 不過你要是這樣做的話,我只能呵呵,時間復(fù)雜度為 O(n) 了,怕是小學(xué)生都會!如果讓你用位運(yùn)算來做,你會怎么做呢? 我舉個例子吧,例如 n = 13,則 n 的二進(jìn)制表示為 1101, 那么 2 的 13 次方可以拆解為: 2^1101 = 2^0001 * 2^0100 * 2^1000。 我們可以通過 & 1和 >>1 來逐位讀取 1101,為1時將該位代表的乘數(shù)累乘到最終結(jié)果。直接看代碼吧,反而容易理解: int pow(int n){時間復(fù)雜度近為 O(logn),而且看起來很牛逼。
5、找出不大于N的最大的2的冪指數(shù)傳統(tǒng)的做法就是讓 1 不斷著乘以 2,代碼如下: 這樣做的話,時間復(fù)雜度是 O(logn),那如果改成位運(yùn)算,該怎么做呢?我剛才說了,如果要弄成位運(yùn)算的方式,很多時候我們把某個數(shù)拆成二進(jìn)制,然后看看有哪些發(fā)現(xiàn)。這里我舉個例子吧。 例如 N = 19,那么轉(zhuǎn)換成二進(jìn)制就是 00010011(這里為了方便,我采用8位的二進(jìn)制來表示)。那么我們要找的數(shù)就是,把二進(jìn)制中最左邊的 1 保留,后面的 1 全部變?yōu)?0。即我們的目標(biāo)數(shù)是 00010000。那么如何獲得這個數(shù)呢?相應(yīng)解法如下: 1、找到最左邊的 1,然后把它右邊的所有 0 變成 1 2、把得到的數(shù)值加 1,可以得到 00100000即 00011111 + 1 = 00100000。 3、把 得到的 00100000 向右移動一位,即可得到 00010000,即 00100000 >> 1 = 00010000。 那么問題來了,第一步中把最左邊 1 中后面的 0 轉(zhuǎn)化為 1 該怎么弄呢?我先給出代碼再解釋吧。下面這段代碼就可以把最左邊 1 中后面的 0 全部轉(zhuǎn)化為 1, n |= n >> 1;就是通過把 n 右移并且做或運(yùn)算即可得到。我解釋下吧,我們假設(shè)最左邊的 1 處于二進(jìn)制位中的第 k 位(從左往右數(shù)),那么把 n 右移一位之后,那么得到的結(jié)果中第 k+1 位也必定為 1,然后把 n 與右移后的結(jié)果做或運(yùn)算,那么得到的結(jié)果中第 k 和 第 k + 1 位必定是 1;同樣的道理,再次把 n 右移兩位,那么得到的結(jié)果中第 k+2和第 k+3 位必定是 1,然后再次做或運(yùn)算,那么就能得到第 k, k+1, k+2, k+3 都是 1,如此往復(fù)下去…. 最終的代碼如下 這種做法的時間復(fù)雜度近似 O(1),重點是,高逼格。 總結(jié)上面講了 5 道題,本來想寫十道的,發(fā)現(xiàn)五道就已經(jīng)寫了好久了,,,,十道的話,怕你們也沒耐心寫完,而且一道比一道難的那種,,,,。 不過呢,我給出的這些例子中,并不是讓你們學(xué)會了這些題就 Ok,而且讓你們有一個意識:很多時候,位運(yùn)算是個不錯的選擇,至少時間效率會快很多,而且高逼格,裝逼必備。所以呢,以后可以多嘗試去使用位運(yùn)算哦,以后我會再給大家找些題來講講,遇到高逼格的,感覺很不錯的,就會拿來供大家學(xué)習(xí)了。 |
|
|