[python3] 56 數(shù)組中只出現(xiàn)一次的數(shù)字

代碼涉及的python3和python2之間的區(qū)別僅僅在于print函數(shù)是否有括號,在挪缶粒客網(wǎng)的python2環(huán)境下不需要print任何輸出蓖康,因此不影響。

題目描述

方法一:使用python內(nèi)collections模塊的Counter函數(shù)

#使用python內(nèi)collections模塊的Counter函數(shù)
# -*- coding:utf-8 -*-
import collections
class Solution:
    # 返回[a,b] 其中ab是出現(xiàn)一次的兩個數(shù)字
    def FindNumsAppearOnce(self, array):
        # write code here
        resultList = []
        key_dict = {}
        key_dict = collections.Counter(array)
        for key, value in key_dict.items():
            if value == 1:
                resultList.append(key)
        return resultList

當(dāng)然澳眷,劍指offer上還有另外一個條件:要求時間復(fù)雜度是o(n), 空間復(fù)雜度是o(1)。上面的程序需要一個額外的字典來存儲每個字母出現(xiàn)的次數(shù)蛉艾,因此明顯不符合空間復(fù)雜度o(1)的要求钳踊。

方法二:使用位運(yùn)算

對于這個問題,我們先考慮一個簡化版的假設(shè)勿侯。若一個整形數(shù)組里拓瞪,除了一個數(shù)字,其它數(shù)字都出現(xiàn)了兩次助琐,請找出這個只出現(xiàn)一次的數(shù)字祭埂。此時,可以用異或的思維兵钮,使得所有出現(xiàn)兩次的數(shù)字在異或操作中變成0.

因此蛆橡,本題將數(shù)組分成兩個子數(shù)組,使得每個子數(shù)組中只包含一個只出現(xiàn)一次的數(shù)字掘譬。那么如何將數(shù)組分成兩個我們想要的子數(shù)組呢泰演?

若我們將原數(shù)組的所有元素都進(jìn)行異或操作,那么結(jié)果肯定是不相同的那兩個元素的異或結(jié)果(如[1, 2, 4, 6, 2, 1]屁药,異或結(jié)果為0000 0100 ^ 0000 0110 = 0000 0010 = 2)粥血,說明不相同的兩個元素在倒數(shù)第二位一定不同(一個1另一個是0)柏锄,因此可以按照這一位的數(shù)字將原數(shù)組分成兩個子數(shù)組酿箭,第一個子數(shù)組中倒數(shù)第二位是1复亏,第二個子數(shù)組中倒數(shù)第二位是0。當(dāng)然異或結(jié)果可能不止一個1缭嫡,可以從前往后數(shù)以第一個1來劃分缔御,也可以從后往前數(shù)。由于相同的兩個元素倒數(shù)第二位一定相同妇蛀,因此相同的兩個元素肯定在同一個子數(shù)組里耕突。

接下來的代碼以{2, 4, 3, 6, 3, 2, 5, 5}為例,不相同的兩位數(shù)字是4评架, 6眷茁,異或結(jié)果為0000 0010,因此以倒數(shù)第二位為標(biāo)準(zhǔn)纵诞,將原數(shù)組中的所有元素分為兩個子數(shù)組上祈。第一個子數(shù)組的倒數(shù)第二位是1,{2, 2, 3, 3, 6}, 第二個子數(shù)組的倒數(shù)第二位是0浙芙, {4登刺, 5, 5}嗡呼。

class Solution:
    # 返回[a, b], 其中a ,b 是其中只出現(xiàn)一次的兩個數(shù)字
    def FindNumsAppearOnce(self, array):
        outputRes = []
        arrayOfOne = [] 
        arrayOfZero = []
        temp = array[0]
        flagNum = 1
        # 循環(huán)纸俭,使得整個數(shù)組的每個元素都參與異或,從而凸顯出只出現(xiàn)一次的兩個元素的異或值
        for i in range(1, len(array)):
            temp ^= array[i] #循環(huán)完成之后的temp南窗,即為有標(biāo)志位的二進(jìn)制數(shù), 依據(jù)其中1的位置將數(shù)組分為兩個子數(shù)組
        #print("temp = ", temp)
        #構(gòu)造只有一個1的二進(jìn)制數(shù)揍很,這個1和temp中右起第一個1位置一致
        flagNum = temp & (-temp)
        #print("flagNum = ", bin(flagNum))
        #劃分兩個子數(shù)組,分別讓每個元素都和構(gòu)造的有標(biāo)志位的二進(jìn)制數(shù)進(jìn)行按位與操作
        for j in range(0, len(array)):
            if array[j] & flagNum == 0:
                arrayOfZero.append(array[j])
            else:
                arrayOfOne.append(array[j])
        #print(arrayOfZero)
        #print(arrayOfOne)
        # 此時已經(jīng)將原數(shù)組按照標(biāo)志位分成了兩個子數(shù)組
        temp = arrayOfZero[0]
        for m in range(1, len(arrayOfZero)):
            #print(arrayOfZero[m])
            temp ^= arrayOfZero[m]
        #print("temp = ", temp)
        outputRes.append(temp)
        temp = arrayOfOne[0]
        for n in range(1, len(arrayOfOne)):
            temp ^= arrayOfOne[n]
        outputRes.append(temp)
        return outputRes
通過啦~

但是這里用了兩個額外的數(shù)組來存儲兩個子數(shù)組万伤,可以省略掉存儲子數(shù)組這一步窒悔,直接計(jì)算子數(shù)組的異或值。具體代碼如下:

class Solution:
    # 返回[a, b], 其中a ,b 是其中只出現(xiàn)一次的兩個數(shù)字
    def FindNumsAppearOnce(self, array):
        outputRes = []
        arrayOfOne = [] 
        arrayOfZero = []
        outputA = 0
        outputB = 0
        temp = array[0]
        flagNum = 1
        # 循環(huán)壕翩,使得整個數(shù)組的每個元素都參與異或蛉迹,從而凸顯出只出現(xiàn)一次的兩個元素的異或值
        for i in range(1, len(array)):
            temp ^= array[i] #循環(huán)完成之后的temp,即為有標(biāo)志位的二進(jìn)制數(shù), 依據(jù)其中1的位置將數(shù)組分為兩個子數(shù)組
        #print("temp = ", temp)
        #構(gòu)造只有一個1的二進(jìn)制數(shù)放妈,這個1和temp中右起第一個1位置一致
        flagNum = temp & (-temp)
        #print("flagNum = ", bin(flagNum))
        #劃分兩個子數(shù)組北救,分別讓每個元素都和構(gòu)造的有標(biāo)志位的二進(jìn)制數(shù)進(jìn)行按位與操作
        for j in range(0, len(array)):
            if array[j] & flagNum == 0:
                #arrayOfZero.append(array[j])
                outputB ^= array[j] #這里為outputA和outputB設(shè)置初始值為0并不影響最終的異或結(jié)果,但是不能設(shè)置為1
            else:
                #arrayOfOne.append(array[j])
                outputA ^= array[j]
        outputRes.append(outputA)
        outputRes.append(outputB)
        return outputRes

2. 修改題目

在一個數(shù)組中除一個數(shù)字只出現(xiàn)一次之外芜抒,其它數(shù)字都出現(xiàn)了三次珍策。請找出那個只出現(xiàn)一次的數(shù)字。

先不考慮只出現(xiàn)一次的那個數(shù)字宅倒,若當(dāng)前數(shù)組中所有數(shù)字都出現(xiàn)了3次攘宙,若以32位來表示每個數(shù)字,如下面示例。

{3, 3, 3}--二進(jìn)制表示如下:
{00000000 00000000 00000000 00000011,
? 00000000 00000000 00000000 00000011蹭劈,
? 00000000 00000000 00000000 00000011}

如果將二進(jìn)制表示的每一位分別求和疗绣,則和都可以被3整除。上面3個數(shù)的和為:
? 00000000 00000000 00000000 00000033

若此時有一個數(shù)字只出現(xiàn)了一次铺韧,
{3, 3, 3, 4}
{00000000 00000000 00000000 00000011,
? 00000000 00000000 00000000 00000011多矮,
? 00000000 00000000 00000000 00000011
? 00000000 00000000 00000000 00000100}, 其和為:
? 00000000 00000000 00000000 00000133}
其中,倒數(shù)第一位和倒數(shù)第二位都為3哈打,可以被3整除塔逃,說明只出現(xiàn)了一次的那個數(shù)字這兩位都是0。倒數(shù)第三位是1料仗, 不可以被3整除湾盗,說明只出現(xiàn)了一次的那個數(shù)字這一位是1。

按照以上的想法來看立轧,代碼如下:

class Solution(object):
    def FindNumAppearOnce(self, array):
        result = [0] * 32 #最后這個只出現(xiàn)一次的數(shù)字的二進(jìn)制形式是一個字符串
        #array = list(map(bin, array))
        flag = 1
        for i in range(31, 0, -1):
            #print("flag = ", bin(flag))
            for j in range(0, len(array)):
                #print("array[%s] = %s" %(j, array[j]))
                if array[j] & flag != 0: #若當(dāng)前數(shù)字當(dāng)前位等于1格粪,則給result的當(dāng)前位加1
                    result[i] += 1
                    #print("result[%s] = %s" %(i, result[i]))
            flag = flag << 1
            if result[i] % 3 == 0:
                result[i] = 0
            else:
                result[i] = 1
        #print("reault = ", result)
        result = list(map(str, result))
        outputResult = "".join(result)
        print(int(outputResult, 2)) #二進(jìn)制字符串轉(zhuǎn)十進(jìn)制,輸出4

if __name__ == "__main__":
    s = Solution()
    outputRes = []
    inputArray = [3, 3, 3, 4]
    outputRes = s.FindNumAppearOnce(inputArray)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肺孵,一起剝皮案震驚了整個濱河市匀借,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌平窘,老刑警劉巖吓肋,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瑰艘,居然都是意外死亡是鬼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門紫新,熙熙樓的掌柜王于貴愁眉苦臉地迎上來均蜜,“玉大人,你說我怎么就攤上這事芒率《诙” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵偶芍,是天一觀的道長充择。 經(jīng)常有香客問我,道長匪蟀,這世上最難降的妖魔是什么椎麦? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮材彪,結(jié)果婚禮上观挎,老公的妹妹穿的比我還像新娘琴儿。我一直安慰自己,他們只是感情好嘁捷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布造成。 她就那樣靜靜地躺著,像睡著了一般普气。 火紅的嫁衣襯著肌膚如雪谜疤。 梳的紋絲不亂的頭發(fā)上佃延,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天现诀,我揣著相機(jī)與錄音,去河邊找鬼履肃。 笑死仔沿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尺棋。 我是一名探鬼主播封锉,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膘螟!你這毒婦竟也來了成福?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤荆残,失蹤者是張志新(化名)和其女友劉穎奴艾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體内斯,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蕴潦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俘闯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潭苞。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖真朗,靈堂內(nèi)的尸體忽然破棺而出此疹,到底是詐尸還是另有隱情,我是刑警寧澤遮婶,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布蝗碎,位于F島的核電站,受9級特大地震影響蹭睡,放射性物質(zhì)發(fā)生泄漏衍菱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一肩豁、第九天 我趴在偏房一處隱蔽的房頂上張望脊串。 院中可真熱鬧辫呻,春花似錦、人聲如沸琼锋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缕坎。三九已至怖侦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谜叹,已是汗流浹背匾寝。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荷腊,地道東北人艳悔。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像女仰,于是被迫代替她去往敵國和親猜年。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351