剑指offerr 56只出现一次的数字_系列(java)

只出现一次的数字系列

  • 异或的性质
  1. 两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是
  2. 如果同一位的数字相同则为 0,不同则为 1
  • 异或的规律
  1. 任何数和本身异或则为 0

  2. 任何数和 0 异或是 本身

  3. 异或满足交换律。 即 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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章