题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为 O(n),空间复杂度为 O(1)。
例如,输入 {2, 4, 3, 6, 3, 2, 5, 5},输出为 4, 6.
先考虑这个数组中只有一个数字只出现了一次,其余的数字出现两次的情况。异或运算的性质:任何一个数字异或它本身等于 0 。也就是说如果从头到尾异或数组中的每一个数字,最后得到的恰好是只出现一次的那个数字,因为那些成对出现的数字再异或中被抵消了。
试着将原来的数组拆分成两个数组,每个数组中包含一个只出现一次的数字,而其他数字都是成对出现的。如果能够这样拆分数组,就可以按照前面的办法分别找出两个只出现一次的数字。
还是从头到尾异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部被抵消了。由于这两个数字肯定不一样,那么异或的结果也就不为 0,也就是说在这个结果数字的二进制表示中至少有一位为 1 。在结果中找到第一个为 1 的位置,记为第 n 位。现在以第 n 位是不是为 1 为标准将原来的数组分成两个数组,第一个数组中的数字的第 n 为 均为 1;第二个数组中的数字的第 n 位均为 0 。那么出现两次的数字肯定被分到同一个数组中。因为两个相同的数字的任意一位都是相同的。于是,就把原来的数组划分为两个子数组,每个子数组中只有一个数字只出现一次,其他的数字都出现了两次。【然后在使用异或运算找出子数组中只出现一次的那个数字】
例如,输入数组为 {2, 4, 3, 6, 3, 2, 5, 5}。在依次对数组中的每一个数字做异或运算之后,得到的结果用二进制表示为 0010 。异或得到的结果中的倒数第二位为 1,因此,我们根据数字的倒数第二位是不是为 1 将数组分为两个子数组 {2, 3, 6, 3, 2} 和 {4, 5, 5}。接下来只要对这两个数组分别做异或就能够找到第一个数组中只出现一次的数字是 6,而第二个数组中只出现一次的数字时 4 。
//输出数组只出现一次的两个数字#includeusing namespace std;int FindFirstBitsIs1(int num){ int indexBit = 0; //记录从最高位算起的最后一个 1 的位置 while((num & 1) == 0 && (indexBit < 8 * sizeof(int))) { num = num >> 1; ++ indexBit; } return indexBit;}bool IsBit1(int num, int indexBit){ num = num >> indexBit; return(num & 1);}//找到数组中只出现一次的那一个数字(该数组中只有一个出现一次的数字,其余的均出现两次)void FindNumberAppearOnce(int *Numbers, int length){ int exclusiveOR = 0; //对数组中的数字依次做异或操作,最后得到的异或结果就是该数组中只出现一次的那个数字 for(int i = 0; i < length; i++) { exclusiveOR ^= Numbers[i]; } cout << exclusiveOR << endl;}//将原来的数组划分为两个子数组,每个子数组中只含有一个只出现一次的数字,其余的数字均出现两次void FindNumsAppearceOnce(int data[], int length){ if(data == NULL || length < 2) return; int resultExclusiveOr = 0; for(int i = 0; i < length; i++) { resultExclusiveOr ^= data[i]; //对数组中的数字依次做异或运算 } int indexOf1 = FindFirstBitsIs1(resultExclusiveOr); //将原来的数组划分为两个子数组Num1[] 和 Num2[],每个子数组中只含有一个只出现一次的数字,其余的数字均出现两次 int Num1[10]; int Num2[10]; int len1 = 0; int len2 = 0; for(int j = 0; j < length; j++) { if(IsBit1(data[j], indexOf1)) { Num1[len1++] = data[j]; } else { Num2[len2++] = data[j]; } } //分别在两个子数组中查找只出现一次的那个数字 FindNumberAppearOnce(Num1, len1); FindNumberAppearOnce(Num2, len2);}int main(){ int data[] = {2, 4, 6, 3, 3, 5, 2, 5, 4, 8}; FindNumsAppearceOnce(data, 10); system("pause"); return 0;}