剑指offer40-数组中只出现一次的数字(Java)

题目来源:https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&&tqId=11193&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
答案来源:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-by-leetcode/

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解题思路

异或方法

  1. 相同数字,异或结果为0
    两个相同的数,转换为二进制后,每一位都相同,根据异或规则,对应位相同为0,所以,两个相同的数做异或操作,结果为0.
    也就是说,数组中出现两次的数字,经异或后,结果为0。

  2. 整个数组异或,相当于,两个只出现一次数字异或
    本题中,除了两个数字(a,b)外,每个数字出现了两次,也就是说,整个数组异或的结果,即 不相同的两个数a,b异或结果,记作res

现在的问题在于:如何根据异或结果,区分这两个数字?

我们都知道,异或规则为相同为0,相异为1。
我们的目标是找到一个分组的规则,使得

  • 两个只出现一次的数字在不同组——A组,B组
  • 出现两次的数字在相同组——可以是A组,也可以是B组,只要确保相同数字在一个组即可(相同数字二进制表示相同,分别与另一个数字进行某种运算,结果也相同)
  • 最后,分别对每一组进行异或操作,得到只出现一次的数字a,b

那么,怎么实现呢?

  • 首先根据异或结果,找到以哪个数位为依据,进行分组。
    从右向左,找到两个只出现一次数字的 第一个不同位是哪一个
    也就是,找到异或结果,从右向左,第一个为1的数位,利用该位进行分组(左移运算,得到…1…,省略号部分为0,比如0001/0010/0100等),即flag
  • 遍历数组,当前值与flag 进行并运算,结果为0的放在A组,结果为1的放在B组
  • A组整体进行异或,得到a,B组整体进行异或,得到b。
  • a,b即为所求。

代码实现—Java

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        //特殊情况
        if(array.length<=1||array==null)
            return;      
               
        //异或方法
        //两个相同的数每一位都相同,根据异或规则,相同为0,所以,两个相同的数做异或操作,结果为0
        //本题中,除了两个数字(a,b)外,每个数字只出现了一次,也就是说,整个数组异或的结果,即不相同的两个数异或结果
        //现在的问题在于:如何根据异或结果,区分这两个数字
        
        //1.整个数组进行异或操作,要想进行异或操作,数组中至少有两个数字
        //当数组中有两个数字时,即为只出现一次的两个数字
        if(array.length==2){
            num1[0] = array[0];
            num2[0] = array[1];
        }        
        int res = 0;
        for(int i = 0;i<array.length;i++){
            res = res^array[i];
        }
        //此时,res为两个只促销一次数字异或的结果
        
        
        //2.根据异或结果,找到这两个数字,从右至左,第一个不同的数位是哪一个
        int flag = 1;//0001
        //当flag 和 res 进行与运算,结果为0时,flag向左移1位
        while((res&flag)==0)
            flag = flag<<1;
        
        
        //3.根据flag,实现数组分组
        //即 和flag 进行与运算,为0的放在一组,非0放在一组
        //结果初始化
        num1[0] = 0;
        num1[0] = 0;
        for(int i=0;i<array.length;i++){
            if((flag & array[i])==0){
                num1[0] = num1[0]^array[i];
            }
            else
                num2[0] = num2[0]^array[i];
        }   
            
    }
}

时间复杂度:O(n)
空间复杂度:O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值