面試題56:數組中只出現一次的兩個數字
題目要求:
一個整數數組里除了兩個數字出現一次,其他數字都出現兩次。請找出這兩個數字。要求時間復雜度為o(n),空間復雜度為o(1)。
解題思路:
這道題可以看成“數組中只出現一次的一個數字”的延伸。如果所有數字都出現兩次,只有一個數字是出現1次,那么可以通過把所有所有進行異或運算解決。因為x^x = 0。
但如果有兩個數字出現一次,能否轉化成上述問題?依舊把所有數字異或,最終的結果就是那兩個出現一次的數字a,b異或的結果。因為a,b不想等,因此結果肯定不為0,那么結果的二進制表示至少有一位為1,找到那個1的位置p,然后我們就可以根據第p位是否為1將所有的數字分成兩堆,這樣我們就把所有數字分成兩部分,且每部分都是只包含一個只出現一次的數字、其他數字出現兩次,從而將問題轉化為最開始我們討論的“數組中只出現一次的一個數字”問題。
實例分析(以2,4,3,6,3,2,5,5為例):
相關數字的二進制表示為:
2 = 0010 3 = 0011 4 = 0100
5 = 0101 6 = 0110
步驟1 全體異或:2^4^3^6^3^2^5^5 = 4^6 = 0010
步驟2 確定位置:對于0010,從右數的第二位為1,因此可以根據倒數第2位是否為1進行分組
步驟3 進行分組:分成[2,3,6,3,2]和[4,5,5]兩組
步驟4 分組異或:2^3^6^3^2 = 6,4^5^5 = 4,因此結果為4,6。
代碼實現:
package chapter6;
/**
* Created with IntelliJ IDEA
* Author: ryder
* Date : 2017/8/17
* Time : 10:58
* Description:數組中數字出現的次數
* 有兩個數字分別出現一次,其他的都出現兩次,找到這兩個數字
**/
public class P275_NumberAppearOnce {
public static int[] findNumsAppearOnce(int[] data){
int result = 0;
for(int i=0;i<data.length;i++)
result^=data[i];
int indexOf1 = findFirstBit1(result);
int ret[] = new int[]{0,0};
if(indexOf1<0)
return ret;
for(int i=0;i<data.length;i++){
if((data[i]&indexOf1)==0)
ret[0]^=data[i];
else
ret[1]^=data[i];
}
return ret;
}
public static int findFirstBit1(int num){
if(num<0)
return -1;
int indexOf1 = 1;
while (num!=0){
if((num&1)==1)
return indexOf1;
else{
num = num>>1;
indexOf1*=2;
}
}
return -1;
}
public static void main(String[] args){
int[] data = new int[]{2,4,3,6,3,2,5,5};
int[] result = findNumsAppearOnce(data); // 4,6
System.out.println(result[0]);
System.out.println(result[1]);
}
}
運行結果
4
6