n重複陣列,是指數組中的數字都出現n次;
唯一m重複異類數,是指存在唯一乙個沒出現n次,只出現了m次的數;
這裡我簡記它為nx+my問題,求解y,其中m < n,陣列中都是整數;
一直沒有精力刷leetcode,今天查問題無意中看到了leetcode 137:給定乙個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現了三次;找出那個只出現了一次的元素。要求時間複雜度o(n),空間複雜度o(1);
沒有在第一時間想到思路,於是花時間研究了一下;
這是乙個3x+y的問題;先給出最優解:
a, b =0, 0for num in
nums:
a = (a ^ num) & ~b
b = (b ^ num) & ~a
return a
在2x+y問題中,方法是使用異或操作一遍全陣列,最終結果就是那個異類數,但從未深入想過為什麼異或會對2x+y問題有效,因而也就無法想通為什麼直接異或會對3x+y問題無效;
再看一遍異或的特點:
0 ^ 0 =01 ^ 0 = 10 ^ 1 = 1
1 ^ 1 = 0
仔細看看,這是因為異或可以讓乙個資訊位(前面的那個1bit數),在接收到有效資訊量(後面的那個1bit數,值為1時為有效)的時候,產生二態變化;
因而如果要解決3x+y問題,就需要有乙個可以產生三態變化的操作符,單純的異或是肯定不行的,因為要對乙個資訊位儲存三種狀態,至少需要兩個資訊位,因而在原本的數字變數空間上操作是不夠的;
假定這個三態異或操作用符號^
3表示;然後用兩個資訊位(b, a)儲存狀態,於是三態異或在接收到有效資訊量(1)時的運算特點如下(接收到無效資訊量0時不產生變化):
(0, 0) ^3 1 = (0, 1)(0, 1) ^3 1 = (1, 0)
(1, 0) ^3 1 = (0, 0)
這個三態變化過程,其實是正常的兩位二進位制數進製加法的乙個修改,修改處在於,對於二進位制10,再加1時,直接跳過11回到00;
因而這個狀態變化過程可描述為:
兩位二進位制數的低位a,在高位b為0時,進行二態變化;在高位b為1時,歸0;
兩位二進位制數的高位b,在低位a歸0時,進行二態變化;在低位a變為1時,保持為0;
可見a與b的變化規律相似,都要求對方為0時自己進行二態變化,對方為1時自己為0,於是上面的最優解的計算過程就很容易寫出來了:
a = (a ^ num) & ~bb = (b ^ num) & ~a
等等,為什麼是return a,而不是return b呢?
首先,上面這段**裡的a、b各是乙個數字,它位的每個二進位制位組合起來,儲存了對整個陣列^
3操作過程中每個二進位制位的狀態變化;
因為這個^
3操作過程中,低位a在第一狀態有效,因而要求3x+y問題,需要return a;
因而,如果題目改為:給定乙個非空整數陣列,除了某個元素只出現兩次以外,其餘每個元素均出現了三次;找出那個只出現了兩次的元素。
求解這個3x+2y問題,答案也就出來了,計算過程同上,而因為高位b在第二狀態有效,因而return b即可;
再發散思維,改題目:給定乙個非空整數陣列,除了某個元素只出現兩次以外,其餘每個元素均出現了四次;找出那個只出現了兩次的元素。
沿著上面的思路,需要做乙個四態異或操作符^4,同樣需要兩個資訊位進行狀態儲存,它對於有效資訊(1)的運算特點如下:
(0, 0) ^4 1 = (0, 1)(0, 1) ^4 1 = (1, 0)
(1, 0) ^4 1 = (1, 1)
(1, 1) ^4 1 = (0, 0)
這就是標準的兩位二進位制數加法,狀態變化過程可描述為:
兩位二進位制數的低位a,正常進行二態變化;
兩位二進位制數的高位b,在低位a進製的時候進製二態變化;
於是計算過程可寫為:
old_a =aa = (a ^num)
b = b ^ ((old_a ^ a) & ~a)
其中(old_a ^ a) & ~a,意義為a由1變為0;
由於這個b的計算核心運算元是異或,異或與否合用有危險,不把(old_a ^ a)引進來會出現bug;
因為要求的y出現2次,所以需要找第二狀態時的有效位,return b;
狀態數同時為4,計算過程同4x+2y,返回時找第三狀態時的有效位,因而也return b;
題目修改為:給定乙個非空整數陣列,除了某個元素只出現4次以外,其餘每個元素均出現了5次;找出那個只出現了4次的元素。
狀態數上公升到5,需要做乙個五態異或操作符^5,需要3個資訊位(c, b, a)做狀態儲存,對有效資訊(1)的運算特點如下:
(0, 0, 0) ^5 1 = (0, 0, 1)(0, 0, 1) ^5 1 = (0, 1, 0)
(0, 1, 0) ^5 1 = (0, 1, 1)
(0, 1, 1) ^5 1 = (1, 0, 0)
(1, 0, 0) ^5 1 = (0, 0, 0)
狀態變化過程可描述為:
三位二進位制數的低位a,在高位c為0時,進行二態變化;在高位c為1時,歸0;
三位二進位制數的中位b,在高位c為0時,低位a進製的時候進製二態變化;在高位c為1時,歸0;
三位二進位制數的高位c,在低位a和中位b同時歸0時,進行二態變化;
計算過程可寫為:
old_a =aa = (a ^ num) & ~c
b = (b ^ ((old_a ^ a) & ~a)) & ~c
c = (c ^ num) & ~b & ~a
由於y出現的次數是4,高位c在狀態4時有效,因而返回c;
測試**:
defx3_y1(nums):
a, b =0, 0
for num in
nums:
a = (a ^ num) & ~b
b = (b ^ num) & ~a
return
adef
x3_y2(nums):
a, b =0, 0
for num in
nums:
a = (a ^ num) & ~b
b = (b ^ num) & ~a
return
bdef
x4_y2(nums):
a, b =0, 0
for num in
nums:
old_a =a
a = (a ^num)
b = b ^ ((old_a ^ a) & ~a)
return
bdef
x4_y3(nums):
return
x4_y2(nums)
defx5_y4(nums):
a, b, c =0, 0, 0
for num in
nums:
old_a =a
a = (a ^ num) & ~c
b = (b ^ ((old_a ^ a) & ~a)) & ~c
c = (c ^ num) & ~b & ~a
return
cprint (x3_y1([2, 3, 2, 2])) # 3
print (x3_y2([2, 3, 2, 3, 2])) # 3
print (x4_y2([2, 2, 3, 2, 3, 2])) # 3
print (x4_y3([2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 2])) #4
print (x4_y3([5, 5, 8, 5, 8, 8, 5])) # 8
print (x5_y4([5, 5, 8, 5, 8, 8, 5, 8, 5])) # 8
執行結果:
333488
乙個陣列中找重複數
乙個大小為n的陣列,裡面的數都屬於範圍 0,n 1 有不確定的重複元素,找到至少乙個重複元素,要求o 1 空間和o n 時間。include const int no repeat flag 1 int findrepeatnumberinarray int a,int n return no re...
封裝乙個方法,找出陣列中重複數大於n的元素集合
例如 1,1,1,2,2,2,3,3,3,3,4,4 封裝乙個陣列原型上的方法,方法返回 重複數目大於2 的子元素集合,結果為 1,2,3 初看並不難,迴圈一下就可以搞定 var arr 1,1,1,2,2,2,3,3,3,3,4,4 var obj var result for var i 0 i...
C C 面試之演算法系列 去除陣列中的重複數字
去除陣列中的重複數字 題目 有乙個陣列 t 100 存放了1 99之間的數字,用效率較高的 把重複數字去掉。例如陣列變成。申請標誌陣列 此題重複的數字可能不只乙個,上述求和的方法不行了。因為是高效率,我們可以採用空間換時間的策略來解決。設立訪問標誌數字,初始化為 0,訪問到 n時將標誌數字的第 n個...