2018.11.5 《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门
这道题原书要求时间复杂度o(n),空间复杂度o(1)。还是特别难的。
这道题的解题思路是异或,数组中成对的数据异或的结果为0,如果只有一个只出现一次的数字i,那整个数组挨个异或之后结果就为i;如果有两个只出现一次的数字i和j,那么整个数组挨个异或之后的结果就是i和j异或的结果。
那么我们就需要想办法把整个数组分为两个,里面分别有i和j,并且其余都是成对的数字。这里采用的方法是,对于i和j异或的结果,其必然有至少1位不是0,取其中一个非0位loc,将整个数组的所有数字按loc是否为1进行划分,分得结果可以满足要求。
整体步骤如下:
1. 计算所有数字的异或结果,结果必为两个单独的数异或的结果且不为0。
2. 找出异或二进制结果中为1的最低位。
3. 将所有数字按照这一位是否为1分为2组,每组中必定包含成对的数字且分别包含两个单独数字。
4. 对两个数组所有数字求异或的结果为其包含的单独数字。
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
Java实现:
/**
*
* @author ChopinXBP
* 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
*
*/
import java.util.ArrayList;
public class FindNumsAppearOnce_38 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int array[] = {2, 4, 3, 6, 3, 2, 5, 5};
int[] num1 = new int[1];
int[] num2 = new int[1];
FindNumsAppearOnce(array, num1, num2);
System.out.println(num1[0] + " " + num2[0]);
}
public static void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
if(array == null || num1 == null || num2 == null)return;
//计算所有数字的异或结果,结果必为两个单独的数异或的结果且不为0
int xorresult = array[0];
for(int i = 1; i < array.length; i++){
xorresult ^= array[i];
}
//找出异或二进制结果中为1的最低位
int loc = 0;
while(xorresult != 0){
if((xorresult & 0x01) == 1)break;
xorresult = xorresult >> 1;
loc ++;
}
//将所有数字按照这一位是否为1分为2组,每组中必定包含成对的数字且分别包含两个单独数字
int num = 1;
while(loc > 0){
num = num << 1;
loc--;
}
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
for(int i = 0; i < array.length; i++){
if((array[i] & num) == 0){
list1.add(array[i]);
}else{
list2.add(array[i]);
}
}
//对两个数组所有数字求异或的结果为其包含的单独数字
xorresult = list1.get(0);
for(int i = 1; i < list1.size(); i++){
xorresult ^= list1.get(i);
}
num1[0] = xorresult;
xorresult = list2.get(0);
for(int i = 1; i < list2.size(); i++){
xorresult ^= list2.get(i);
}
num2[0] = xorresult;
}
}
C++实现示例:
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
if(data.size() < 2) return ;
int myxor = 0;
int flag = 1;
for(int i = 0 ; i < data.size(); ++ i )
myxor ^= data[i];
while((myxor & flag) == 0) flag <<= 1;
*num1 = myxor;
*num2 = myxor;
for(int i = 0; i < data.size(); ++ i ){
if((flag & data[i]) == 0) *num2 ^= data[i];
else *num1 ^= data[i];
}
}
};
测试代码:
// ====================测试代码====================
void Test(char* testName, int data[], int length, int expected1, int expected2)
{
if(testName != NULL)
printf("%s begins: ", testName);
int result1, result2;
FindNumsAppearOnce(data, length, &result1, &result2);
if((expected1 == result1 && expected2 == result2) ||
(expected2 == result1 && expected1 == result2))
printf("Passed.\n\n");
else
printf("Failed.\n\n");
}
void Test1()
{
int data[] = {2, 4, 3, 6, 3, 2, 5, 5};
Test("Test1", data, sizeof(data) / sizeof(int), 4, 6);
}
void Test2()
{
int data[] = {4, 6};
Test("Test2", data, sizeof(data) / sizeof(int), 4, 6);
}
void Test3()
{
int data[] = {4, 6, 1, 1, 1, 1};
Test("Test3", data, sizeof(data) / sizeof(int), 4, 6);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
return 0;
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#