算法(12):位操作

按位操作是真的復(fù)雜,且技巧性很高,特此專(zhuān)門(mén)開(kāi)一篇,來(lái)簡(jiǎn)單講解。



原碼棚唆、反碼、補(bǔ)碼講解

LeetCode 位操作例題 (C++版本)

1. 原碼

原碼就是符號(hào)位加上真值的絕對(duì)值, 即用第一位表示符號(hào), 其余位表示值. 比如如果是8位二進(jìn)制:

[+1] = 0000 0001
[-1] = 1000 0001

第一位是符號(hào)位. 因?yàn)榈谝晃皇欠?hào)位, 所以8位二進(jìn)制數(shù)的取值范圍就是[-127 , 127]心例,即:

[1111 1111 , 0111 1111]

2. 反碼
反碼的表示方法是:
正數(shù)的反碼是其本身
負(fù)數(shù)的反碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變瑟俭,其余各個(gè)位取反.

[+1] = [00000001] = [00000001]
[-1] = [10000001] = [11111110]

3. 補(bǔ)碼
補(bǔ)碼的表示方法是:
正數(shù)的補(bǔ)碼就是其本身
負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變, 其余各位取反, 最后+1. (即在反碼的基礎(chǔ)上+1)

[+1] = [00000001] = [00000001] = [00000001]補(bǔ)
[-1] = [10000001] = [11111110] = [11111111]補(bǔ)

??計(jì)算機(jī)儲(chǔ)存數(shù)據(jù)都是用補(bǔ)碼的方式儲(chǔ)存的,c++里面獲取某個(gè)數(shù)的二進(jìn)制表示(也就是補(bǔ)碼)如下:cout << bitset<sizeof(int) *8> (num) << endl;


例題1: 計(jì)算一個(gè)數(shù)轉(zhuǎn)化為2進(jìn)制之后契邀,包含多少1摆寄。( 又稱(chēng)之為 Hamming weight,漢明重量)
輸入:5
輸出:2(0101)
解釋?zhuān)?br> n = n & (n-1)可以消除二進(jìn)制位最右邊的一個(gè)1坯门。
如:0101 & 0100微饥,結(jié)果為 0100, 和 0101相比古戴,第四位的1消失了欠橘,計(jì)數(shù)器加1;
再繼續(xù)0100 & 0011现恼,結(jié)果為 0000肃续, 和0100相比,第二位的1消失了叉袍,計(jì)數(shù)器加1始锚;
此時(shí)n=0,跳出循環(huán)喳逛。

def solution(n):
    count = 0
    while n:
        n = n & (n-1)
        count += 1
    return count

ans = solution(15)
print(ans)

例題2:判斷一個(gè)數(shù)是否為2的指數(shù)冪瞧捌。
輸入:8
輸出:True

解釋?zhuān)?br> 1的二進(jìn)制為1
2的二進(jìn)制為10
4的二進(jìn)制為100
8的二進(jìn)制為1000
發(fā)現(xiàn)只有最高位為1其余位為0,如果將其減一的話那么最高位為0其余位則為1,兩者相與的結(jié)果則必定為0
結(jié)論:如果 a&(a-1) == 0 則a必定是2的指數(shù)冪

def solution(n):
    return (n & (n - 1)) == 0

ans = solution(16)
print(ans)


例題3:判斷一個(gè)數(shù)是否為4的指數(shù)冪姐呐。
輸入:16
輸出:True

解釋?zhuān)?br> 1的二進(jìn)制為1
4的二進(jìn)制為100
16的二進(jìn)制為10000
第一句不多說(shuō)殿怜,(num & (num - 1)) == 0用來(lái)判斷是不是2的指數(shù)冪,第二句則是去除掉是2的指數(shù)冪曙砂,但不是4的指數(shù)冪的部分头谜。

def solution(num):
    return (num & (num - 1)) == 0 and (num - 1) % 3 == 0 
    #或者下面這種寫(xiě)法也行
    # return (n&(n-1)) == 0 and n & 0x55555555

ans = solution(16)
print(ans)

例題4:兩整數(shù)求和。
輸入:-1鸠澈,1
輸出:0

解釋?zhuān)?br> 下面代碼中 a 儲(chǔ)存不包含進(jìn)位的計(jì)算結(jié)果柱告,即(a ^ b)
b 只儲(chǔ)存進(jìn)位的結(jié)果款侵,即(a & b) << 1)
當(dāng)b等于0時(shí)侧纯,代表沒(méi)有進(jìn)位新锈,跳出循環(huán)。

至于mask的作用眶熬,因?yàn)槔碚撋?python 里 int 類(lèi)型的位數(shù)是沒(méi)有上限的妹笆,不同與c++,int為32位娜氏。而最后一句的~(a ^ mask)拳缠,其實(shí)在二進(jìn)制位上,執(zhí)行前后是的結(jié)果是一樣的贸弥,但表達(dá)含義變化了窟坐,將第一位變?yōu)榱朔?hào)位。
如 4294967276(大于2的31次方)绵疲,其二進(jìn)制表示為:
...11111111111111111111111111101100(...表示前面還有很多位哲鸳,因?yàn)?int 類(lèi)型的位數(shù)是沒(méi)有上限),
a ^ mask得:
00000000000000000000000000010011(此時(shí)前面的都mask掉了盔憨,第一位為符號(hào)位徙菠,該數(shù)為19)
^求反得:
11111111111111111111111111101100(和4294967276的二進(jìn)制完全相同,但是含義變成了-20)

class Solution:
    def getSum(self, a, b):
        """
        :type a: int
        :type b: int
        :rtype: int
        """
        # 32 bits integer max
        MAX = 0x7FFFFFFF
        # 32 bits interger min
        MIN = 0x80000000
        # mask to get last 32 bits
        mask = 0xFFFFFFFF
        while b != 0:
            # ^ get different bits and & gets double 1s, << moves carry
            a, b = (a ^ b) & mask, ((a & b) << 1) & mask
        # if a is negative, get a's 32 bits complement positive first
        # then get 32-bit positive's Python complement negative
        return a if a <= MAX else ~(a ^ mask)


例題5:缺失的數(shù)字
給一個(gè)長(zhǎng)度為n的數(shù)組郁岩,里面存放從0到n的數(shù)婿奔,但是少了一個(gè),求出該數(shù)问慎。
輸入:[0萍摊,1,2如叼,4记餐,5]
輸出:3
解釋?zhuān)簩?duì)0和某一個(gè)數(shù)進(jìn)行異或操作,則結(jié)果為該數(shù)薇正;
某個(gè)數(shù)對(duì)另一個(gè)數(shù)進(jìn)行兩次 ^異或操作片酝,則結(jié)果還是該數(shù)本身囚衔。

def solution(arr):
    ans = 0
    for i,num in enumerate(arr):
        ans ^= num
        ans ^= i
    return ans^len(arr)

ans = solution([0,1,2,4,5])
print(ans)


例題6:將一個(gè)32位無(wú)符號(hào)整數(shù),進(jìn)行按位翻轉(zhuǎn)
輸入:12
輸出:805306368
解釋?zhuān)?br> 00000000000000000000000000001100 (12的32進(jìn)制表示)
00110000000000000000000000000000 (805306368的32進(jìn)制表示)

def solution(n):
    ans = 0
    mask = 1<<31
    while n:
        if n&1:
            ans |= mask
        mask >>= 1
        n>>= 1
    return ans

n = 12
print(bin(n)[2:].zfill(32))
ans = solution(n)
print(bin(ans)[2:].zfill(32))

例題7:逐個(gè)元素進(jìn)行and 操作雕沿。給出一個(gè)區(qū)間[m, n]练湿,且 0 <= m <= n <= 2147483647,對(duì)m到n之間的每個(gè)元素進(jìn)行與操作审轮,返回操作的結(jié)果肥哎。
輸入:[5,7]
輸出:4 (101 &110 & 111)
解釋?zhuān)簩?和7一直左移,并不斷判斷左移后兩個(gè)數(shù)是否相等疾渣,如果相等(此時(shí)已經(jīng)左移兩次篡诽,1==1),則跳出循環(huán)榴捡,然后右移相同位數(shù)(兩位)杈女,并返回結(jié)果,即4吊圾。

def solution(m,n):
    a = 0
    while m!=n:
        m>>=1
        n>>=1
        a += 1
    return m<<a

ans = solution(5,7)
print(ans)

多運(yùn)算符混合應(yīng)用題目

例題8: DNA重復(fù)序列达椰。某DNA序列由ACGT四個(gè)字母組成,需要找出里面所有長(zhǎng)度為10项乒,且出現(xiàn)重復(fù)的序列啰劲。
輸入:'AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT'
輸出:['AAAAACCCCC', 'CCCCCAAAAA']

輸入:’AAAAAAAAAAA'
輸出:['AAAAAAAAAA']

附注:注意里面的運(yùn)算優(yōu)先級(jí),首先 +/-優(yōu)先級(jí)最大檀何,其次<< 蝇裤, 隨后是 &,| 的優(yōu)先級(jí)最小频鉴。

def solution(s):
    d = {}
    ans = []
    num = 0
    if len(s)<11: return []
    for i in range(9):
        num = num<<3 | ord(s[i]) - ord('A')+1 & 7
        # 上面相當(dāng)于 num = (num<<3) | ((ord(s[i]) - ord('A')+1) & 7)

    for i in range(9,len(s)):
        num = num << 3 & 0x3fffffff | (ord(s[i]) - ord('A')+1) & 7
        if d.get(num) == 1:
            ans.append(s[i-9:i+1])
        d[num]=d.get(num,0)+1
    return ans


ans = solution('AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT')
print(ans)

例題9:單獨(dú)數(shù)字猖辫。給出一個(gè)數(shù)組,該數(shù)組中有兩個(gè)元素只出現(xiàn)一次砚殿,其他的都出現(xiàn)兩次啃憎,請(qǐng)找出這兩個(gè)元素。
輸入:[1,1,2,2,3,4]
輸出:[3,4]

解釋?zhuān)旱谝徊剿蒲祝瑢?duì)所有元素進(jìn)行異或操作辛萍,某一位出現(xiàn)1的次數(shù)為奇數(shù)次,才會(huì)為1羡藐;
第二步贩毕,bit = bit & ~(bit - 1),找出最右邊第一位為1的二進(jìn)制位仆嗦,其余全置零辉阶。
(eg:bit = 101100,執(zhí)行完之后,bit = 000100)谆甜;
第三步垃僚,再次遍歷數(shù)組,通過(guò)該比特位將數(shù)據(jù)分為兩部分规辱,這兩部分各包含一個(gè)只出現(xiàn)一次的元素谆棺,以及若干個(gè)成對(duì)出現(xiàn)的元素。

def solution(arr):
    bit = 0
    for i in arr:
        bit ^=i

    bit = bit & ~(bit - 1)
    ans =[0,0]
    for i in arr:
        if bit & i:
            ans[0]^=i
        else:
            ans[1]^=i
    return ans

ans = solution([1,2,3,5,1,3])
print(ans)


例題10:單獨(dú)數(shù)字罕袋。給一個(gè)數(shù)組改淑,其中只有一個(gè)元素出現(xiàn)一次,其他都出現(xiàn)了三次浴讯,找出該元素朵夏。
輸入:[1,1,1,3,4,4,4]
輸出:3
解釋?zhuān)海ù祟}解法通用性很強(qiáng),題目可以改成:一個(gè)元素出現(xiàn)m次榆纽,其他元素都出現(xiàn)了k次仰猖,只要m不為k的整數(shù)倍即可用該題方法來(lái)求解。)

def solution(arr):
    barr = [0] * 32
    for n in arr:
        for i in range(0, 32):
            if n & (1 << i):
                barr[i] = (barr[i] + 1) % 3

    ans = 0
    for i in range(0, 32):
        ans = ans | barr[i] << i
    return ans if ans <= 0x7fffffff else ~(ans ^ 0xffffffff)

ans = solution([1,1,1,-3])
print(ans)

例題11:單詞長(zhǎng)度的乘積掠河。給定一個(gè)包含各種單詞的數(shù)組亮元,返回一個(gè)最大值猛计,該最大值為:兩個(gè)不含相同字符的單詞唠摹,其長(zhǎng)度的乘積最大值(即len(word[i]) * len(word[j])) 。
輸入:["a","ab","abc","d","cd","bcd","abcd"]
輸出:4 ( len('ab') * len('cd') = 4)

輸入: ["a","aa","aaa","aaaa"]
輸出: 0 (找不到滿足條件的一對(duì)單詞)

def maxProduct(words) :
    mask = [0] * len(words)
    ans = 0
    for i in range(len(words)):
        for c in words[i]:
            mask[i] |= 1 << ord(c) - ord('a')
        for j in range(i):
            if not mask[i] & mask[j]:
                ans = max(ans, len(words[i]) * len(words[j]))
    return ans

ans = maxProduct(["abcw","baz","foo","bar","xtfn","abcdef"])
print(ans)

例題12: 求一個(gè)集合所有子集奉瘤。(這應(yīng)該是最后一題了勾拉,,盗温,)
輸入:[2,3,5]
輸出:[[], [2], [3], [2, 3], [5], [2, 5], [3, 5], [2, 3, 5]]
解釋?zhuān)?br> 對(duì)于集合當(dāng)中每個(gè)數(shù)字藕赞,都有要、不要兩種選擇卖局,所以子集個(gè)數(shù)為 2的len(arr)次方 (即下面的 size)斧蜕。
注意下面的關(guān)鍵句if (1 << j & i),該句子可以實(shí)現(xiàn)如下取舍方式:
對(duì)于第一個(gè)數(shù)砚偶,1 << j = 1, 二進(jìn)制為0001批销,與 i 進(jìn)行按位與操作后,其取舍方式為:
0染坯,1均芽,0,1单鹿,0掀宋,1,0,1...
對(duì)于第二個(gè)數(shù)劲妙,1 << j = 2, 二進(jìn)制為0010湃鹊,與 i 進(jìn)行按位與操作后,其取舍方式為:
0是趴,0涛舍,1,1唆途,0富雅,0,1肛搬,1...
對(duì)于第三個(gè)數(shù)没佑,1 << j = 4, 二進(jìn)制為0100,與 i 進(jìn)行按位與操作后温赔,其取舍方式為:
0蛤奢,0,0陶贼,0啤贩,1,1拜秧,1痹屹,1...

def solution(arr):
    size = 1 << len(arr)
    print(size)
    ans = [[] for i in range(size)]
    for i in range(len(ans)):
        for j in range(len(arr)):
            if (1 << j & i):
                ans[i].append(arr[j])
    return ans

ans = solution([2,3,5])
print(ans)


暫且完結(jié),撒花~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枉氮,一起剝皮案震驚了整個(gè)濱河市志衍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聊替,老刑警劉巖楼肪,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惹悄,居然都是意外死亡春叫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)泣港,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)暂殖,“玉大人,你說(shuō)我怎么就攤上這事爷速⊙胄牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵惫东,是天一觀的道長(zhǎng)莉给。 經(jīng)常有香客問(wèn)我毙石,道長(zhǎng),這世上最難降的妖魔是什么颓遏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任徐矩,我火速辦了婚禮,結(jié)果婚禮上叁幢,老公的妹妹穿的比我還像新娘滤灯。我一直安慰自己,他們只是感情好曼玩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布溉潭。 她就那樣靜靜地躺著专挪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上犹芹,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天芹啥,我揣著相機(jī)與錄音绩鸣,去河邊找鬼鼎文。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贬墩,可吹牛的內(nèi)容都是我干的榴嗅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼陶舞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嗽测!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吊说,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤论咏,失蹤者是張志新(化名)和其女友劉穎优炬,沒(méi)想到半個(gè)月后颁井,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蠢护,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年雅宾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葵硕。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眉抬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懈凹,到底是詐尸還是另有隱情蜀变,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布介评,位于F島的核電站库北,受9級(jí)特大地震影響爬舰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寒瓦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一情屹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杂腰,春花似錦垃你、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至少辣,卻和暖如春官还,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毒坛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工望伦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煎殷。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓屯伞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親豪直。 傳聞我的和親對(duì)象是個(gè)殘疾皇子劣摇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354