【技巧总结】位运算装逼指南
位算法的效率有多快我就不說(shuō),不信你可以去用 10 億個(gè)數(shù)據(jù)模擬一下,今天給大家講一講位運(yùn)算的一些經(jīng)典例子。不過(guò),最重要的不是看懂了這些例子就好,而是要在以后多去運(yùn)用位運(yùn)算這些技巧,當(dāng)然,采用位運(yùn)算,也是可以裝逼的,不信,你往下看。我會(huì)從最簡(jiǎn)單的講起,一道比一道難度遞增,不過(guò)居然是講技巧,那么也不會(huì)太難,相信你分分鐘看懂。
判斷奇偶數(shù)
判斷一個(gè)數(shù)是基于還是偶數(shù),相信很多人都做過(guò),一般的做法的代碼如下
if( n % 2) == 01// n 是個(gè)奇數(shù) }如果把 n 以二進(jìn)制的形式展示的話,其實(shí)我們只需要判斷最后一個(gè)二進(jìn)制位是 1 還是 0 就行了,如果是 1 的話,代表是奇數(shù),如果是 0 則代表是偶數(shù),所以采用位運(yùn)算的方式的話,代碼如下:
if(n & 1 == 1){// n 是個(gè)奇數(shù)。 }有人可能會(huì)說(shuō),我們寫成 n % 2 的形式,編譯器也會(huì)自動(dòng)幫我們優(yōu)化成位運(yùn)算啊,這個(gè)確實(shí),有些編譯器確實(shí)會(huì)自動(dòng)幫我們優(yōu)化。但是,我們自己能夠采用位運(yùn)算的形式寫出來(lái),當(dāng)然更好了。別人看到你的代碼,我靠,牛逼啊。無(wú)形中還能裝下逼,是不是。當(dāng)然,時(shí)間效率也快很多,不信你去測(cè)試測(cè)試。
2、交換兩個(gè)數(shù)
交換兩個(gè)數(shù)相信很多人天天寫過(guò),我也相信你每次都會(huì)使用一個(gè)額外來(lái)變量來(lái)輔助交換,例如,我們要交換 x 與 y 值,傳統(tǒng)代碼如下:
int tmp = x; x = y; y = tmp;這樣寫有問(wèn)題嗎?沒(méi)問(wèn)題,通俗易懂,萬(wàn)一哪天有人要為難你,**不允許你使用額外的輔助變量來(lái)完成交換呢?**你還別說(shuō),有人面試確實(shí)被問(wèn)過(guò),這個(gè)時(shí)候,位運(yùn)算大法就來(lái)了。代碼如下:
x = x ^ y // (1) y = x ^ y // (2) x = x ^ y // (3)我靠,牛逼!三個(gè)都是 x ^ y,就莫名交換成功了。在此我解釋下吧,我們知道,兩個(gè)相同的數(shù)異或之后結(jié)果會(huì)等于 0,即 n ^ n = 0。并且任何數(shù)與 0 異或等于它本身,即 n ^ 0 = n。所以,解釋如下:
把(1)中的 x 帶入 (2)中的 x,有
y = x^y = (xy)y = x(yy) = x^0 = x。 x 的值成功賦給了 y。
對(duì)于(3),推導(dǎo)如下:
x = x^y = (xy)x = (xx)y = 0^y = y。
這里解釋一下,異或運(yùn)算支持運(yùn)算的交換律和結(jié)合律哦。
以后你要是別人看不懂你的代碼,逼格裝高點(diǎn),就可以在代碼里面采用這樣的公式來(lái)交換兩個(gè)變量的值了,被打了不要找我。
講這個(gè)呢,是想告訴你位運(yùn)算的強(qiáng)大,讓你以后能夠更多著去利用位運(yùn)算去解決一些問(wèn)題,一時(shí)之間學(xué)不會(huì)也沒(méi)事,看多了就學(xué)會(huì)了,不信?繼續(xù)往下看,下面的這幾道題,也是非常常見(jiàn)的,可能你之前也都做過(guò)。
3、找出沒(méi)有重復(fù)的數(shù)
給你一組整型數(shù)據(jù),這些數(shù)據(jù)中,其中有一個(gè)數(shù)只出現(xiàn)了一次,其他的數(shù)都出現(xiàn)了兩次,讓你來(lái)找出一個(gè)數(shù) 。
這道題可能很多人會(huì)用一個(gè)哈希表來(lái)存儲(chǔ),每次存儲(chǔ)的時(shí)候,記錄 某個(gè)數(shù)出現(xiàn)的次數(shù),最后再遍歷哈希表,看看哪個(gè)數(shù)只出現(xiàn)了一次。這種方法的時(shí)間復(fù)雜度為 O(n),空間復(fù)雜度也為 O(n)了。
然而我想告訴你的是,采用位運(yùn)算來(lái)做,絕對(duì)高逼格!
我們剛才說(shuō)過(guò),兩個(gè)相同的數(shù)異或的結(jié)果是 0,一個(gè)數(shù)和 0 異或的結(jié)果是它本身,所以我們把這一組整型全部異或一下,例如這組數(shù)據(jù)是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出現(xiàn)了一次,其他都出現(xiàn)了兩次,把他們?nèi)慨惢蛞幌?#xff0c;結(jié)果如下:
由于異或支持交換律和結(jié)合律,所以:
123451234 = (11)(22)(33)(44)5= 00005 = 5。
也就是說(shuō),那些出現(xiàn)了兩次的數(shù)異或之后會(huì)變成0,那個(gè)出現(xiàn)一次的數(shù),和 0 異或之后就等于它本身。就問(wèn)這個(gè)解法牛不牛逼?所以代碼如下
int find(int[] arr){int tmp = arr[0];for(int i = 1;i < arr.length; i++){tmp = tmp ^ arr[i];}return tmp; }時(shí)間復(fù)雜度為 O(n),空間復(fù)雜度為 O(1),而且看起來(lái)很牛逼。
4、m的n次方
如果讓你求解 m 的 n 次方,并且不能使用系統(tǒng)自帶的 pow 函數(shù),你會(huì)怎么做呢?這還不簡(jiǎn)單,連續(xù)讓 n 個(gè) m 相乘就行了,代碼如下:
int pow(int n){int tmp = 1;for(int i = 1; i <= n; i++) {tmp = tmp * m;}return tmp; }不過(guò)你要是這樣做的話,我只能呵呵,時(shí)間復(fù)雜度為 O(n) 了,怕是小學(xué)生都會(huì)!如果讓你用位運(yùn)算來(lái)做,你會(huì)怎么做呢?
我舉個(gè)例子吧,例如 n = 13,則 n 的二進(jìn)制表示為 1101, 那么 m 的 13 次方可以拆解為:
m^1101 = m^0001 * m^0100 * m^1000。
我們可以通過(guò) & 1和 >>1 來(lái)逐位讀取 1101,為1時(shí)將該位代表的乘數(shù)累乘到最終結(jié)果。直接看代碼吧,反而容易理解:
int pow(int n){int sum = 1;int tmp = m;while(n != 0){if(n & 1 == 1){sum *= tmp;}tmp *= tmp;n = n >> 1;}return sum; }時(shí)間復(fù)雜度近為 O(logn),而且看起來(lái)很牛逼。
這里說(shuō)一下,位運(yùn)算很多情況下都是很二進(jìn)制扯上關(guān)系的,所以我們要判斷是否是否位運(yùn)算,很多情況下都會(huì)把他們拆分成二進(jìn)制,然后觀察特性,或者就是利用與,或,異或的特性來(lái)觀察,總之,我覺(jué)得多看一些例子,加上自己多動(dòng)手,就比較容易上手了。所以呢,繼續(xù)往下看,注意,先別看答案,先看看自己會(huì)不會(huì)做。
5、找出不大于N的最大的2的冪指數(shù)
傳統(tǒng)的做法就是讓 1 不斷著乘以 2,代碼如下:
int findN(int N){int sum = 1;while(true){if(sum * 2 > N){return sum;}sum = sum * 2;} }這樣做的話,時(shí)間復(fù)雜度是 O(logn),那如果改成位運(yùn)算,該怎么做呢?我剛才說(shuō)了,如果要弄成位運(yùn)算的方式,很多時(shí)候我們把某個(gè)數(shù)拆成二進(jìn)制,然后看看有哪些發(fā)現(xiàn)。這里我舉個(gè)例子吧。
例如 N = 19,那么轉(zhuǎn)換成二進(jìn)制就是 00010011(這里為了方便,我采用8位的二進(jìn)制來(lái)表示)。那么我們要找的數(shù)就是,把二進(jìn)制中最左邊的 1 保留,后面的 1 全部變?yōu)?0。即我們的目標(biāo)數(shù)是 00010000。那么如何獲得這個(gè)數(shù)呢?相應(yīng)解法如下:
1、找到最左邊的 1,然后把它右邊的所有 0 變成 1
2、把得到的數(shù)值加 1,可以得到 00100000即 00011111 + 1 = 00100000。
3、把 得到的 00100000 向右移動(dòng)一位,即可得到 00010000,即 00100000 >> 1 = 00010000。
那么問(wèn)題來(lái)了,第一步中把最左邊 1 中后面的 0 轉(zhuǎn)化為 1 該怎么弄呢?我先給出代碼再解釋吧。下面這段代碼就可以把最左邊 1 中后面的 0 全部轉(zhuǎn)化為 1,
n |= n >> 1; n |= n >> 2; n |= n >> 4;就是通過(guò)把 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ù)下去…
最終的代碼如下
int findN(int n){n |= n >> 1;n |= n >> 2;n |= n >> 4;n |= n >> 8 // 整型一般是 32 位,上面我是假設(shè) 8 位。return (n + 1) >> 1; }這種做法的時(shí)間復(fù)雜度近似 O(1),重點(diǎn)是,高逼格。
總結(jié)
上面講了 5 道題,本來(lái)想寫十道的,發(fā)現(xiàn)五道就已經(jīng)寫了好久了,,,,十道的話,怕你們也沒(méi)耐心寫完,而且一道比一道難的那種,,,,。
不過(guò)呢,我給出的這些例子中,并不是讓你們學(xué)會(huì)了這些題就 Ok,而且讓你們有一個(gè)意識(shí):很多時(shí)候,位運(yùn)算是個(gè)不錯(cuò)的選擇,至少時(shí)間效率會(huì)快很多,而且高逼格,裝逼必備。所以呢,以后可以多嘗試去使用位運(yùn)算哦,以后我會(huì)再給大家找些題來(lái)講講,遇到高逼格的,感覺(jué)很不錯(cuò)的,就會(huì)拿來(lái)供大家學(xué)習(xí)了。
兄dei,如果覺(jué)得我寫的不錯(cuò),不妨幫個(gè)忙
1、關(guān)注我的原創(chuàng)微信公眾號(hào)「帥地玩編程」,每天準(zhǔn)時(shí)推送干貨技術(shù)文章,專注于寫算法 + 計(jì)算機(jī)基礎(chǔ)知識(shí)(計(jì)算機(jī)網(wǎng)絡(luò)+ 操作系統(tǒng)+數(shù)據(jù)庫(kù)+Linux),聽(tīng)說(shuō)關(guān)注了的不優(yōu)秀也會(huì)變得優(yōu)秀哦。
2、給俺點(diǎn)個(gè)贊唄,可以讓更多的人看到這篇文章,順便激勵(lì)下我,嘻嘻。
作者簡(jiǎn)潔
作者:大家好,我是帥地,從大學(xué)、自學(xué)一路走來(lái),深知算法,計(jì)算機(jī)基礎(chǔ)知識(shí)的重要性,所以申請(qǐng)了一個(gè)微星公眾號(hào)『帥地玩編程』,專業(yè)于寫這些底層知識(shí),提升我們的內(nèi)功,帥地期待你的關(guān)注,和我一起學(xué)習(xí)。 轉(zhuǎn)載說(shuō)明:未獲得授權(quán),禁止轉(zhuǎn)載
總結(jié)
以上是生活随笔為你收集整理的【技巧总结】位运算装逼指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2012年4月份第3周51Aspx源码发
- 下一篇: Jedis 读写效率测试 hgetAll