目录
题目一
题目描述
数组中只出现一次的两个数字。
一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
测试用例
功能测试(数组中有多对重复的数字;数组中没有重复的数字)
解题思路
1、小技巧:任何数异或自己为0,并且0异或任何数等于其数本身。 即,a ⊕ a = 0 、b ⊕ 0 = b;
2、假设数组只有一个出现一次的数字:从头到尾异或数组的元素,根据1,相同元素抵消为0,剩下的结果为只出现一次的元素;
3、想办法把题目中的数组分成两个子数组,子数组都满足2中的条件
神奇的技巧:
1)首先得到题目中的数字依次异或的结果;如{2,2,3,3,4,5,5,6} 异或结果为0010;
2)倒数第二位为1,根据这个标准,即倒数第二位是不是1,将原数组分为两组:{2,2,3,3,6} 、 {4,5,5},这就得到满足2中的两个子数组了。
原因:出现两次的数字一定分配到同一个子数组,不同的两个数字也会分到不同子数组。
参考解题
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
// 子问题: 1、数组中只有一个出现一次的数字,找出这个数字;
// 2、将有两个出现一次数字的数组划分为两个子数组,转换为子问题1
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
// 异常
if(array == null || array.length < 2){
num1[0] = 0;
num2[0] = 0;
}
// 所有数字异或结果
int resultOfXor = 0;
for(int i = 0 ; i < array.length; ++i ){
resultOfXor ^= array[i];
}
// 异或结果右边第一个为1的位数
int indexOf1 = FindFirstBitsIs1(resultOfXor);
// 分组,该位数为1的为一组,不为1的为一组,然后分别异或
num1[0] = 0;
num2[0] = 0;
for(int j = 0 ; j < array.length ; ++j){
if( isBit1(array[j], indexOf1) ){
num1[0] ^= array[j];
}else{
num2[0] ^= array[j];
}
}
}
// ---在整数num的二进制中找到最右边是1的位------
public int FindFirstBitsIs1(int num){
int index = 0 ;
// int类型二进制有几位?32位
while((num & 1) == 0 && index < 32){
num = num >> 1;
index++;
}
return index;
}
// ---判断整数num从右边开始的第indexOf1位是不是1-----
public boolean isBit1(int num, int indexOf1){
num = num >> indexOf1;
return ((num & 1) == 1); // 1 & 1 = 1
}
}
题目二
题目描述
数组中唯一只出现一次的数字
在一个数组中除一个数字值出现一次之外,其他数字都出现了三次,请找出那个只出现一次的数字。
测试用例
功能测试(唯一值出现一次的数字分别为0、正数、负数;重复出现三次的数字分别是0、正数、负数)
解题思路
3个相同的数字异或结果还是该数字,并不能使用异或很好的解题。转换思路,使用位运算。
概述:把数组所有数字(二进制表示)的每一位相加,如果该位能够被3整除,表示只出现一次的m在该位为0,否则在该位为1。举例,比如{2,2,2,3,3,3,4}
2 | 1 | 0 | |
2 | 1 | 0 | |
2 | 1 | 0 | |
3 | 1 | 1 | |
3 | 1 | 1 | |
3 | 1 | 1 | |
目标数m(4) | 1 | 0 | 0 |
intSum | 1 | 6 | 3 |
intSum该位能否被3整除 | 不能 | 能 | 能 |
intSum该位对3取余 | 1 | 0 | 0 |
目标数m该位是什么 | 1 | 0 | 0 |
参考解题
package Sword;
/**
*
* @author tyler
*
*/
public class AppearOnceOther3times {
public int findNumberAppearOnce(int[] array) throws Exception {
// 异常
if (array == null || array.length == 0) {
throw new Exception("非法输入");
}
// 求intSum数组
int[] bitSum = new int[32];
for (int i = 0; i < array.length; ++i) {
// 外循环,遍历数组每一个数字
for (int j = 31; j >= 0; --j) {
// 内循环,判断数组中某个数的各个位是否为1,是则对intSum加1
if ((array[i] & 1) == 1) {
bitSum[j]++;
}
array[i] = array[i] >> 1;
}
}
// 根据规律,生成目标数字
int result = 0;
for (int i = 31; i >= 0; --i) {
result = result >> 1;
result += bitSum[i] % 3;
}
return result;
}
}