只出现一次的数字

题目:输入一个整数数组,数组中只有一个数字出现了一次,而其他数字都出现了3次。请找出那个只出现一次的数字。例如,如果输入的数组为[0,1,0,1,0,1,100],则只出现一次的数字是100。

该题目有个简化的类似题目“输入数组中除一个数字只出现一次之外其他数字都出现两次,请找出那个只出现一次的数字”。对于这个简化版本,有三种方法可以解决,第一种就是两次for循环,第二种可以利用hashset的不重复的特点,第三种是利用异或(^)运算。这里稍稍赘述异或运算的一点细节:
0^0=0
0^a=a
a^a=0
a^b^a=b
 

import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class SingleNumber01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        //去掉键盘输入的字符串中的"["和"]"
        str = str.substring(1, str.length() - 1);
        //将字符串以","分割,转为一个字符串数组
        String[] s = str.split(",");
        int[] arr = new int[s.length];
        for (int i = 0; i < arr.length; i++)
            arr[i] = Integer.parseInt(s[i]);
        System.out.println(findNum1(arr));
        System.out.println("-----------------");
        System.out.println(findNum2(arr));
        System.out.println("-----------------");
        System.out.println(findNum3(arr));
    }


    public static Integer findNum1(int[] arr){
        for(int i = 0; i < arr.length; i++){
            for(int j = 0; j <= arr.length; j++){
                //注意循环结束的条件,实际运行时j不会超出数组范围
                if(i == j)
                    continue;
                //因为这里的判断,让代码运行时在超出范围前就结束了循环
                if(j == arr.length)
                    return arr[i];
                if(arr[i] == arr[j])
                    break;
            }
        }
        return null;
    }

    public static Integer findNum2(int[] arr) {
        Set<Integer> set = new HashSet<>();
        //增强for循环遍历数组
        for (int i : arr) {
            //添加失败,说明集合中存在重复元素
            if (!set.add(i))
                //移除该重复元素
                set.remove(i);
        }
        if (set.isEmpty()) return null;
        return set.toArray(new Integer[0])[0];
    }

    public static int findNum3(int[] arr) {
        int flag = 0;
        for (int i : arr) {
            flag ^= i;
        }
        return flag;
    }
}

但对于此题目,三个相同数字异或运算并不能消除该数字,所以需要更换方法。

三个相同数字,它们的和一定是三的倍数(有点废话哈哈),换言之,余数一定为0。由此,不难想到其二进制形式也该满足这个道理,任何数位相加之和能被3整除。所以在该题目中,若所有数字第i个数位之和被3整除,说明这个只出现一次的数字第i个数位一定是0。同理,若所有数字第i个数位之和被3除余1,说明这个只出现一次的数字第i个数位一定是1。基此得出:

import java.util.Scanner;

public class SingleNumber02 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        //去掉键盘输入的字符串中的"["和"]"
        str = str.substring(1, str.length() - 1);
        //将字符串以","分割,转为一个字符串数组
        String[] s = str.split(",");
        int[] arr = new int[s.length];
        for (int i = 0; i < arr.length; i++)
            arr[i] = Integer.parseInt(s[i]);
        System.out.println(singleNumber(arr));
    }


    public static int singleNumber(int[] arr) {
        //用于保存arr中所有整数的二进制形式第i个数位之和
        int[] bitSums = new int[32];
        for (int num : arr) {
            for (int i = 0; i < 32; i++) {
                //整数num先右移31-i位,原来从左数第i个数位右移后位于最右边
                //然后与1做位与运算,即%2运算
                bitSums[i] += (num >> (31 - i) & 1);
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            //从高位初步恢复该只出现一次的数字
            result = (result << 1) + bitSums[i] % 3;
        }
        return result;
    }
}

由此,同类型的题目“输入一个整数数组,数组中只有一个数字出现m次,其他数字都出现n次。请找除那个唯一出现m次的数字。假设m不能被n整除”都可以用这种方法:如果数组中所有的数字的第i个数位相加之和能被n整除,那么出现m次的数字的第i个数位一定是0;否则出现m次的数字的第i个数位一定是1。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不过是条河鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值