日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法之美 : 位运算

發(fā)布時間:2023/11/29 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法之美 : 位运算 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一小節(jié)我們用三道題了解一下面試過程中棧和隊列的常見面試題。本小節(jié)筆者將通過幾個 位運(yùn)算 的題目來帶大家熟悉下常用的位運(yùn)算知識。

相比于棧和隊列來講,筆者自身認(rèn)為位運(yùn)算需要掌握的知識就要多一些,包括對于數(shù)字的二進(jìn)制表示,二進(jìn)制的反碼,補(bǔ)碼。以及二進(jìn)制的常見運(yùn)算都需要了解。當(dāng)然如果系統(tǒng)的去學(xué),可能沒有經(jīng)歷,也可能即使學(xué)完了,仍舊不會做題。所以筆者認(rèn)為通過直接去刷一些相應(yīng)的題目,則是一個比較便捷的途徑。

給定一個整數(shù),請寫一個函數(shù)判斷該整數(shù)的奇偶性(?????)

該題目作為后續(xù)題目的鋪墊,看上去還是沒有任何難度的。主要考察了面試能否想到用二進(jìn)制的位運(yùn)算方法去解決。

首先整數(shù)可以分為正數(shù),負(fù)數(shù),0。也可以分為奇數(shù)和偶數(shù)。偶數(shù)的定義是:如果一個數(shù)是2的整數(shù)倍數(shù),那么這個數(shù)便是偶數(shù)。如果不使用位運(yùn)算的方法,我們完全可以使用下面的方式解決:

public boolean isOdd(int num){//odd 奇數(shù)return num % 2 != 0; } 復(fù)制代碼

可是面試題不可能去簡單就考察這么簡單的解法,進(jìn)而我們想到了二進(jìn)制中如果 一個數(shù)是偶數(shù)那么最后一個一定是 0 如果一個數(shù)是奇數(shù)那么最后一位一定是 1;而十進(jìn)制 1 在 8 位二進(jìn)制中表示為 0000 0001,我們只需將一個數(shù)個 1相與(&) 得到的結(jié)果如果是 1 則表示該數(shù)為奇數(shù),否知為偶數(shù)。所以這道題的最佳解法如下:

public boolean isOdd(int num){return num & 1 != 0; } 復(fù)制代碼#include "iostream" using namespace std; //聲明 bool IsOdd(int num);bool IsOdd(int num) {int res = (num & 1);return res != 0; } 復(fù)制代碼

測試:

int main(int argc, const char * argv[]) {std::cout << "是否是奇數(shù) : " << IsOdd(1) <<endl;std::cout << "是否是奇數(shù) : " << IsOdd(4) <<endl;return 0; }//結(jié)果 是否是奇數(shù) : 1//是 true 是否是奇數(shù) : 0//不是 false 復(fù)制代碼

同樣給定一個整數(shù),請寫一個函數(shù)判斷該整數(shù)是不是2的整數(shù)次冪(?????)

這道題仍舊考察面試者對于一個數(shù)的二進(jìn)制的表示特點(diǎn),一個整數(shù)如果是2的整數(shù)次冪,那么他用二進(jìn)制表示完肯定有唯一一位為1其余各位都為 0,形如 0..0100...0。比如 8 是 2的3次冪,那么這個數(shù)表示為二進(jìn)制位 0000 1000 。

除此之外我們還應(yīng)該想到,一個二進(jìn)制如果表示為 0..0100...0,那么它減去1得到的數(shù)二進(jìn)制表示肯定是 0..0011..1 的形式。那么這個數(shù)與自自己減一后的數(shù)相與得到結(jié)果肯定為0。

如:

所以該題最佳解法為:

public boolean log2(int num){return (num & (num - 1)) == 0; } 復(fù)制代碼#include "iostream" using namespace std; //聲明 bool IsLog2(int num); //定義 bool IsLog2(int num) {return (num & (num -1)) == 0; } 復(fù)制代碼

測試:

int main(int argc, const char * argv[]) {std::cout << "是否是2的整數(shù)次冪 : " << IsLog2(1) <<endl;std::cout << "是否是2的整數(shù)次冪 : " << IsLog2(3) <<endl;return 0; }//結(jié)果 是否是2的整數(shù)次冪 : 1 //是 true 是否是2的整數(shù)次冪 : 0 //不是 false 復(fù)制代碼

給定一個整數(shù),請寫一個函數(shù)判斷該整數(shù)的二進(jìn)制表示中1的個數(shù)(?????)

此題較之上一題又再進(jìn)一步,判斷一個整數(shù)二進(jìn)制表示中1的個數(shù),假設(shè)這個整數(shù)用32位表示,可正可負(fù)可0,那么這個數(shù)中有多少個1,就需要考慮到符號位的問題了。

相信讀者應(yīng)該都能想到最近基本的解法即通過右移運(yùn)算后與 1 相與得到的結(jié)果來計算結(jié)果,如果采用這種解法,那么這個題的陷阱就在于存在負(fù)數(shù)的情況,如果負(fù)數(shù)的話標(biāo)志位應(yīng)該算一個1。所以右移的時候一定要采用無符號右移才能得到正確的解法。

ps 對于正數(shù)右移和無符號右移得到結(jié)果一樣,如果是負(fù)數(shù),右移操作將在二進(jìn)制補(bǔ)碼左邊添加追加1,而無符號右移則是補(bǔ) 0 。

所以此題一種解法如下:

public int count1(int n) {int res = 0;while (n != 0) {res += n & 1;n >>>= 1;}return res; } 復(fù)制代碼#include "iostream" using namespace std;//注意C++中沒有無符號右移操作,所以這里傳入一個 unsigned 數(shù)作為 params int count1(unsigned int n){int res = 0;while(n != 0){res += n & 1;n >>= 1;}return res; } 復(fù)制代碼

測試結(jié)果:

int main(int argc, const char * argv[]) {std::cout << "二進(jìn)制中1的個數(shù) : " << count1(-1) <<endl;std::cout << "二進(jìn)制中1的個數(shù) : " << count1(1) <<endl;return 0; }//結(jié)果 二進(jìn)制中1的個數(shù) : 32 二進(jìn)制中1的個數(shù) : 1 復(fù)制代碼

能回答出上邊的答案你的面試肯定是及格了,但是作為練習(xí)來說,是否有額外的解法呢?首先上述結(jié)果最壞的情況可能需要循環(huán)32次。上面我們算過一道如何判斷一個數(shù)是否是2的整數(shù)倍,我們用過了 n&(n-1)==0 的方法。其實(shí)該題的第二個解法也可以用這個方法。為什么呢?我們開看一次上邊的圖:

我們是否能發(fā)現(xiàn),每次與比自己小1的數(shù)與那么該數(shù)的二進(jìn)制表示最后一個為1位上的1將將會被抹去。其實(shí)這是一個知道有這種原理才能想到的方法,所以大家也不用哀嘆說我怎么想不到,通過這次記住有這個規(guī)律下次就多一個思路也不是很么壞事。

下面我們來看下判斷一個數(shù)中有多少個1的完整圖解:

所以我們可以通過如下方法來得到題解,這樣我們可以減少移動次數(shù)

public int countA(int n){int res = 0;while(n != 0){n &= (n - 1);res++;}return res; } 復(fù)制代碼#include "iostream" using namespace std; // 同上傳入無符號整數(shù) int countA(unsigned int n){int res = 0;while(n != 0){n &= (n - 1);res++;}return res; } 復(fù)制代碼

測試結(jié)果:

int main(int argc, const char * argv[]) {std::cout << "二進(jìn)制中1的個數(shù) : " << countA(-1) <<endl;std::cout << "二進(jìn)制中1的個數(shù) : " << countA(1) <<endl;return 0; }//結(jié)果 二進(jìn)制中1的個數(shù) : 32 二進(jìn)制中1的個數(shù) : 1 復(fù)制代碼

在其他數(shù)都出現(xiàn)兩次的數(shù)組中找到只出現(xiàn)一次的那個數(shù)(?????)

這道題同樣是考察為位運(yùn)算的一道題,但是如果對于不熟悉位運(yùn)算的朋友可能壓根都不會往這方面想,也許當(dāng)場直接就下邊寫下了遍歷數(shù)組記每個數(shù)出現(xiàn)次數(shù)的代碼了。其實(shí)這道題要求在時間復(fù)雜度在O(n) 空間復(fù)雜度為O(1)的條件下,那種解法是不符合要求的。我們來看下為位運(yùn)算的解題思路。

首先我們應(yīng)該知道二進(jìn)制異或操作,異或結(jié)果是二進(jìn)制中兩個位相同為0,相異為1。因此可以有個規(guī)律:

任何整數(shù) n 與 0 異或總等于其本身 n,一個數(shù)與其本身異或那么結(jié)果肯定是 0。

還需要知道一個規(guī)律:

多個數(shù)異或操作,遵循交換律和結(jié)合律。

對于第一條朋友們肯定都很好理解,然而第二條規(guī)律才是這道題的解題關(guān)鍵。如果我們有一個變量 eO = 0 那么在遍歷數(shù)組過程中,使每個數(shù)與 eO 異或得到的值在賦值給額 eO 即 eO=eO ^ num 那么遍歷結(jié)束后eO的值一定是那個出現(xiàn)一次的數(shù)的值。這是為什么呢?我們可以舉個例子:

假設(shè)有這么一個序列: C B D A A B C 其中只有 D 出現(xiàn)一次,那么因?yàn)楫惢驖M足交換律和結(jié)合律,所以我們遍歷異或此序列的過程等價于

eO ^ (A ^ A ^ B ^ B ^ C ^ C ) ^ D = eO ^ 0 ^ D = D 復(fù)制代碼

所以對于任何排列的數(shù)組,如果只有一個數(shù)只出現(xiàn)了奇數(shù)次,其他的數(shù)都出現(xiàn)了歐數(shù)次,那么最終異或的結(jié)果肯定為出現(xiàn)奇數(shù)次的那個數(shù)。

所以此題可以有下面的這種解法:

java 解法

public int oddTimesNum(int[] arr) {int eO = 0;for (int cur : arr) {eO = eO ^ cur;}return eO; } 復(fù)制代碼

C++ 解法

int oddTimesNum(vector<int> arr) {int eO = 0;for (int cur : arr) {eO = eO ^ cur;}return eO; } 復(fù)制代碼

測試:

int main(int argc, const char * argv[]) {vector<int> arr = {2,1,3,3,2,1,4,5,4};std::cout << "出現(xiàn)奇數(shù)次的那個數(shù): " << oddTimesNum(arr) <<endl;return 0; }//結(jié)果 出現(xiàn)奇數(shù)次的那個數(shù): 5 復(fù)制代碼

關(guān)于這道題還有個延伸版本,就是如果數(shù)組中出現(xiàn)1次的數(shù)有兩個,那么該如何得到這兩個數(shù)。

在其他數(shù)都出現(xiàn)兩次的數(shù)組中找到只出現(xiàn)一次的那兩個數(shù)(?????)

我們順著上題的思路來思考,如果有兩個數(shù)獲得的結(jié)果 eO 肯定是 eO = a^b,此題的關(guān)鍵就在于如何分別得到 a,b 這兩個數(shù)。我們應(yīng)該想到,任何不相同的兩個除了跟自己異或外,不可能每一個位都相同,也就是說不相同的兩個數(shù) a b 異或得到結(jié)果二進(jìn)制表示上肯定有一位為 1。 這是關(guān)鍵。

我們可以假設(shè)第 k 位不為 0 ,那么就說明 a 與 b 在這位上數(shù)值不相同。我們要做只是設(shè)置一個數(shù)第 k 位 為 1,其余位為 0 記為 rightOne。

這時需要拿 eOhasOne = 0 再異或遍歷一次數(shù)組,但是需要忽略與 rightOne 相與等于 0 的數(shù)。因?yàn)橄嗯c等于 0 則代表了這個數(shù)肯定是兩個數(shù)中第 k 位不為 1的那個。最終得到的 eOhasOne 就是 a b 中第 k 為為 1 的那個。

那么接下來就剩下一個問題要解決了,如何找到 rightOne ,這里采用與本身補(bǔ)碼相與的方法得到即 int rightOne = eO & (~eO + 1) 。

可以參照下圖來理解下整個過程:

我們來看下最終的代碼:

java 寫法

public void printOddTimesNum(int[] arr) {int eO = 0;int eOhasOne = 0;for (int cur : arr) {eO = eO ^ cur;}int rightOne = eO & (~eO + 1);for (int cur : arr) {if ((rightOne & cur) != 0) {eOhasOne = eOhasOne ^ cur;}}System.out.println("eOhasOne = " + eOhasOne + " " + (eOhasOne ^ eO)); } 復(fù)制代碼

C++ 寫法

void printOddTimesNum(vector<int> arr) {int eO = 0;int eOhasOne = 0;for (int cur : arr) {eO = eO ^ cur;}int rightOne = eO & (~eO + 1);for (int cur : arr) {if ((cur & rightOne) != 0) {eOhasOne = eOhasOne ^ cur;}}std::cout<<"一個出現(xiàn)1次的數(shù) " << eOhasOne << endl;std::cout<<"二個出現(xiàn)1次的數(shù) " << (eO ^ eOhasOne) <<endl; } 復(fù)制代碼

測試:

int main(int argc, const char * argv[]) {vector<int> arr1 = {2,1,3,3,2,1,4,5};printOddTimesNum(arr1);return 0; } //結(jié)果: 一個出現(xiàn)1次的數(shù) 5 二個出現(xiàn)1次的數(shù) 4 復(fù)制代碼

參考

《劍指 offer 第二版》 《程序員代碼面試指南 - 左程云》

歡迎關(guān)注我的微信公眾號,接收第一手技術(shù)干貨

總結(jié)

以上是生活随笔為你收集整理的算法之美 : 位运算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 天天操综合网 | 亚欧洲精品在线视频 | 视频在线免费观看 | freesex性hd公交车上 | 大地av | 亚洲色图综合网 | 欧美一级日韩一级 | 国产免费的av | 久久人人视频 | 亚洲国产v| 日日骚一区 | 9i看片成人免费看片 | 熟女少妇内射日韩亚洲 | 中文字幕丰满人伦在线 | 天天干天天操天天干 | 91蝌蚪视频在线 | 国产欧美激情在线观看 | 国产精品呻吟久久 | 亚洲天堂一区二区三区 | 在哪里看毛片 | 亚洲欧洲国产日韩 | 国产精品一级片在线观看 | 男人你懂的网站 | 欧美伦乱 | 午夜在线观看影院 | 日本中文字幕网 | 草莓巧克力香氛动漫的观看方法 | 青青草免费公开视频 | 免费网站在线观看视频 | 欧美xxxxx自由摘花 | 国产91精品露脸国语对白 | 亚洲骚图| 黄色网战入口 | 欧美综合亚洲 | 亚洲性大片 | 97中文字幕| 欧美暧暧视频 | 麻豆成人精品国产免费 | xxx一区二区 | 亚洲av高清一区二区三区 | 97精品熟女少妇一区二区三区 | 精品少妇人妻av免费久久洗澡 | 桃谷绘里香在线播放 | 日本少妇激情 | 深爱激情av | 91免费视频入口 | 在线免费观看一区二区三区 | 激情内射人妻1区2区3区 | 性欧美精品中出 | 久草资源在线视频 | 91超薄丝袜肉丝一区二区 | 玩日本老头很兴奋xxxx | 亚洲毛片在线 | 欧美呦呦呦 | 制服丝袜中文字幕在线 | 丰满少妇一区 | 婷婷激情影院 | 澳门色网 | www亚洲天堂 | 4438x全国最大成人 | 久久伊人色 | 99re6在线精品视频免费播放 | 国产又粗又硬 | 啪啪小视频 | 91xxxxx| 高清视频一区二区三区 | 日韩久久不卡 | 亚洲久久色 | 国产超碰人人爽人人做人人爱 | 日韩一级片免费 | 高清久久久 | 操操操免费视频 | 久热草| 国产黄色在线 | 97综合视频 | aaa亚洲精品 | 光棍影院av | 成人在线免费播放视频 | 性综艺节目av在线播放 | 日韩啪啪网站 | zzji欧美大片 | 中文字幕免费在线播放 | 国产日韩亚洲欧美 | 中文字幕第9页 | 日本视频h | 麻豆专区 | 强行无套内谢大学生初次 | 五月色婷婷综合 | 欧美极品少妇 | 在线观看黄 | 国产高潮白浆 | 免费观看av | 欧美sm凌虐视频网站 | 少妇不卡视频 | 999国产视频 | 爱射网| 国产又粗又黄又爽又硬 | 在线观看免费成人 | 亚洲va欧美va国产综合久久 |