[剑指Offer]数组中只出现一次的数字(Java)

题目

1、一个整型数组里除了一个数字之外,其他的数字都出现了两次。找出这个数字

思路:

     * 1、两个相同的数字做异或操作,结果是0
     * 2、数组中所有的数字做异或操作,最后的结果就是出现了一次的唯一数字

 

2、一个整型数组里除了两个数字之外,其他的数字都出现了两次。找出这两个数字

思路:

     * 1、两个相同的数字做异或操作,结果是0
     * 2、数组中所有的数字做异或操作,最后的结果必然不为0
     * 3、找出结果中二进制位中最后一个为1的二进制位n
     * 4、按照二进制位n是否为1分类数组为两份,则1个数字在第一组,两一个必在第二组,且两组中其他的也都是出现了两次的数字
     * 5、分类后分别进行异或操作    
 

代码

package com.codinginterviews.array;
 
/**
 * 题目:
 * 数组中只出现一次的数字 -- newcoder 剑指Offer 40
 * 
 * 题目描述:
 * 一个整型数组里除了两个数字之外,其他的数字都出现了两次。
 * 请写程序找出这两个只出现一次的数字。
 */
public class FindNumsAppearOnce
{
    public void findNumsAppearOnce(int [] array, int num1[] , int num2[]) {
        if (array == null || array.length <= 1) {
            return;
        }
 
        int len = array.length;
        
        // 得到异或结果
        int result = 0;
        for (int i=0; i<len; i++) {
            result ^= array[i];
        }
        
        int flag = 1;
        // 找到右起第一个为1的位置,其余位置赋值为0
        while ((result & flag) == 0) {
        	flag <<= 1;
        }
        
        for (int i=0; i<len; i++) {
        	// 以该位置是否为0分割数组
            if ((flag & array[i]) == 0) {
            	num1[0] ^= array[i];
            } else {
            	num2[0] ^= array[i];
            }
        }
    }
	
	
    /**
     * 思路:
     * 1、两个数字相同,异或结果为0
     * 2、对所有数字取异或,右起找第一个二进制位为1的位置。
     * 3、按照该二进制是否为1,拆分为两个数组,则两个出现一次的不同的数字必然分割在两个数组
     * 4、对两个数组分别进行异或操作即可
     */
    // num1,num2分别为长度为1的数组。传出参数
    // 将num1[0],num2[0]设置为返回结果
    public void findNumsAppearOnceI(int [] array, int num1[] , int num2[]) {
        if (array == null || array.length <= 1) {
            return;
        }
 
        int len = array.length;
        
        // 得到异或结果
        int result = 0;
        for (int i=0; i<len; i++) {
            result ^= array[i];
        }
        
        // 得到右起二进制位位1的索引
        String resultToBinaryString = Integer.toBinaryString(result);
        // 从右开始
        int binaryIdx = resultToBinaryString.length() - 1;
        for (; binaryIdx>=0; binaryIdx--) {
            if (resultToBinaryString.charAt(binaryIdx) == '1') {
                break;
            }
        }
        // 得到右起索引
        binaryIdx = resultToBinaryString.length() - 1 - binaryIdx;
        
        // 以该索引位置的值是否为1分割数组
        int num1Val = 0;
        int num2Val = 0;
        
        for (int i=0; i<len; i++) {
            String str = Integer.toBinaryString(array[i]);
            int strLen = str.length();
            // 通过右起索引 找到真正的索引
            int curIdx = strLen - 1 - binaryIdx;
            if (curIdx > 0 && strLen > curIdx && str.charAt(curIdx) == '1') {
                num1Val ^= array[i];
            } else {
                num2Val ^= array[i];
            }
        }
        
        // 赋值元素到num1[] num2[]
        num1[0] = num1Val;
        num2[0] = num2Val;
    }
 
     
    /**
     * 题目:
     * 数组中除了一个数字,其他的数字都出现两次,找出这个数字
     * 
     * 思路:
     * 1、两个相同的数字做异或操作,结果是0
     * 2、数组中所有的数字做异或操作,最后的结果就是出现了一次的唯一数字
     */
    public int findNumAppearOnce(int[] arr) {
        if (null == arr || arr.length <= 0) {
            return -1;
        }
        
        int result = 0;
        
        for (int i : arr) {
            result ^= i;
        }
        
        return result;
    }
    
    /**
     * 目标:
     * 数组中除了两个数字,其他的数字都出现两次,找出这两个数字
     * 
     * 思路:
     * 1、两个相同的数字做异或操作,结果是0
     * 2、数组中所有的数字做异或操作,最后的结果必然不为0
     * 3、找出结果中二进制位中最后一个为1的二进制位n
     * 4、按照二进制位n是否为1分类数组为两份,则1个数字在第一组,两一个必在第二组,且两组中其他的也都是出现了两次的数字
     * 5、分类后分别进行异或操作
     */
    public void findNumsAppearOnceII(int[] array, int[] num1, int[] num2) {
        if (null == array || array.length <= 1) {
            return;
        }
        
        // 数组中的元素进行异或操作
        int allXOR = 0;
        for (int i : array) {
            allXOR ^= i;
        }
        
        // 获取异或结果转化为二进制 最后一个为1的二进制位
        int index = getLastBinaryOneIndex(allXOR);
        
        
        // 根据数据的倒数index位是否为1来分割数组
        for (int j : array) {
            if (isNumBinaryOneIndex(j, index)) {
                num1[0] ^= j;
            } else {
                num2[0] ^= j;
            }
        }
        
    }
    
    
    /**
     * 获取数字转化为二进制 倒数第几位为1
     * 从1开始计数,如1的返回值为1,2的返回值为2 
     */
    private int getLastBinaryOneIndex(int num) {
        int index = 1;
        while ((num & 1) != 1 && index < 32) {
            num = num >>> index;
            index++;
        }
        return index;
    }
    
    /**
     * 数字的二进制-倒数 binaryOneIndex位是否为1
     * 
     * 为1返回true
     * 为0返回false
     * 
     */
    private boolean isNumBinaryOneIndex(int num, int binaryOneIndex) {
        int numRightIndex = num >>> (binaryOneIndex - 1);
        return (numRightIndex & 1) == 1;
    }
        
    
    public static void main(String[] args)
    {
        int[] array = {4,6,1,1,1,1};
        int[] num1 = new int[1];
        int[] num2 = new int[1];
        new FindNumsAppearOnce().findNumsAppearOnce(array, num1, num2);
        System.out.println(num1[0] + " " + num2[0]);
        
        num1[0] = 0;
        num2[0] = 0;
        new FindNumsAppearOnce().findNumsAppearOnceII(array, num1, num2);
        System.out.println(num1[0] + " " + num2[0]);
    }
 
}

 

参考:

数组中只出现一次的数字:https://www.cnblogs.com/senlinyang/p/7930417.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值