只出现一次的数字系列
- 异或的性质
- 两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是
- 如果同一位的数字相同则为 0,不同则为 1
- 异或的规律
-
任何数和本身异或则为 0
-
任何数和 0 异或是 本身
-
异或满足交换律。 即 a ^ b ^ c ,等价于 a ^ c ^ b
136. 只出现一次的数字 1
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
class Solution {
public int singleNumber(int[] nums) {
int sum = 0;
for(int i =0; i < nums.length; i++){
sum ^= nums[i];
}
return sum;
}
}
面试题56 - I. 数组中数字出现的次数
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字
###方法1 hashmap
- 时间O(N)
- 空间O(N)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结
import java.util.HashMap;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i = 0; i < array.length; i++){
if(map.containsKey(array[i])) map.put(array[i], 2);
else map.put(array[i], 1);
}
int count = 0; //记录num1是否被填充
for(int i = 0; i < array.length; i++){
if(map.get(array[i]) == 1){
if(count == 0){
num1[0] = array[i];
count++;
}else{
num2[0] = array[i];
}
}
}
}
}
方法2 位运算
- 时间O(N)
- 空间O(1)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.HashMap;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int sum =0; // 保存两两以获得结果,其结果等于两个只出现一次的元素的异或值。
//将数组所有元素进行异或,最后的结果一定是那两个单一数字的异或结果。看上图示例
//用示例[4,4,6,1]最后异或结果就是 6和1异或的结果 7
for(int i = 0; i < array.length; i++){
sum ^= array[i];
}
// 记录第一次接获结果其从低位到高位第一个为1的首位。
// 为什么要找它? 以为两个不一样的值 其位运算第一次发生不一样的时候,该位位运算结果就是1,找到该位置
// 可以把整个数组划分为两个部分。
int first = 1;
while((sum & first) == 0){ // 为0进位,为1不执行循环。
first = first << 1;
}
//first为1,所以我们可以根据数组元素的二进制低位第一位是否为1,将数组分为2类,
// 示例数组可以分为 低位第一位为0:[4,4,6] 低位第一位为1:[1]
//此时再将两个数组两两异或就可以得到最终结果。
for(int i = 0; i < array.length; i++){
// 将数组分类
if((array[i] & first) == 0){
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
}
137. 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
// int型数据,最多只有32位。
for(int i = 0; i < 32; i++){
int count = 0;
int bit = 1 << i;
for(int j = 0; j < nums.length; j++){
// 如果三个相同的数在该位为0,count不加
// 如果三个相同的数在该位不为0,count加,那么,如果单独的数与该位与后为0,最后count一定是3的倍数。否则需要作与运算还原该位。
if((bit & nums[j]) != 0){
count++;
}
}
if((count % 3) != 0){
res |= bit;
}
}
return res;
}
}
- 牛客网,需要自定义输入。类名统一用Main!
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int [] nums = new int[n];
for(int i = 0; i < n; i++){
nums[i] = in.nextInt();
}
System.out.print(find(nums));
in.close();
}
public static int find(int [] nums){
int res = 0;
// int型数据,最多只有32位
for(int i = 0; i < 32; i++){
int count = 0;
int bit = 1 << i;
for(int j = 0; j < nums.length; j++){
// 如果三个相同的数在该位为0,count不加
// 如果三个相同的数在该位不为0,count加,那么,如果单独的数与该位与后为0,最后count一定是3的倍数。否则需要作与运算还原该位。
if((bit & nums[j]) != 0){
count++;
}
}
if((count % 3) != 0){
res |= bit;
}
}
return res;
}
}