剑指offer_数组中只出现一次的数字

本文探讨了在整型数组中查找唯一出现一次的数字的多种算法,包括使用Set和异或运算的方法,特别深入讲解了如何通过位运算解决两个数字各出现一次的问题,提供了详细的Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

如果只有一个出现一次的数字,其他都出现两次:

方法1:Set.add+Set.remove

使用Set集合存储,Set集合不存储重复值,add()方法返回值为boolean类型,这一特点可以利用。
Set集合的add方法,添加成功返回true,否则返回false,而Set集合不存储重复值,所以当要添加的数与集合中已存在的数重复时,不会再进行添加操作,返回false,这时再进行remove操作,将集合中已存在的那个与要添加的数相同的元素移除,这样将作为方法参数传递过来的整型数组遍历完后,到最后集合中就只剩下了那个只出现了一次的数字。

方法2: 异或

使用异或运算符^,0与其他数字异或的结果是那个数字,相等的数字异或得0。要操作的数组中除了某个数字只出现了一次之外,其他数字都出现了两次,所以可以定义一个变量赋初始值为0,用这个变量与数组中每个数字做异或运算,并将这个变量值更新为那个运算结果,直到数组遍历完毕,最后得到的变量的值就是数组中只出现了一次的数字了。这种方法只需遍历一次数组,提高了程序运行的效率。
--------------------- 
原文:https://blog.youkuaiyun.com/young_simple/article/details/83152888 

如果有2个出现一次的数字,其他都出现两次:

方法1:ArrayList.add+ArrayList.add.remove+ArrayList.contains

1. 遍历数组,使用一个ArrayList记录当前只出现了一次的值。 
2. 若当前遍历的值,在ArrayList中已经出现,则移除该值,继续遍历。 
3. 最后剩下的两个值,即为所求。

方法2:分两组位运算 

1. 除了有两个数字只出现了一次,其他数字都出现了两次。异或运算中,任何一个数字和自己本身异或都是0,任何一个数字和0异或都是本身。 
2. 如果尝试把原数组分成两个子数组,且刚好每个子数组中各自包含一个只出现一次的数字。则在该前提下,每个子数组中,只有一个数字出现了一次,其他数字都出现了两次。 
3. 针对每个子数组,从头到尾依次异或每个数字,则最后留下来的就是只出现了一次的数字。因为出现两次的都抵消掉了。 
4. 怎样实现子数组的划分呢。若对原数组从头到尾的进行异或,则最后得到的结果就是两个只出现一次的数字的异或运算结果。这个结果的二进制表示中,至少有一位为1,因为这两个数不相同。该位记为从最低位开始计数的第n位。 
5. 则分组的标准定为从最低位开始计数的第n位是否为1。因为出现两次的同一个数字,各个位数上都是相同的,所以一定被分到同一个子数组中,且每个子数组中只包含一个出现一次的数字。
--------------------- 
原文:https://blog.youkuaiyun.com/ouyangyanlan/article/details/72668012 

以下是自己写的java版本代码:

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        //只有两位的特殊情况处理
        int len=array.length;
        if(len == 2){
            num1[0] = array[0];
            num2[0] = array[1];
            return;
        }
        //以flag异或累积出两个值的异或结果
        int flag=0;
        for(int i=0;i<len;i++){
            flag^=array[i];
        }
        //保留异或结果中的最右边一个1位
        int site=flag-(flag&(flag-1));
        //在分组的过程中直接处理,不必再储存两个分组
        num1[0]=0;
        num2[0]=0;
        for(int i=0;i<len;i++){
            int temp=site;
            if((site&array[i])==site){
                num1[0]^=array[i];
            }
            else{
                num2[0]^=array[i];
            }
        }
        return;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值