【位运算技巧】异或运算符(2)数组中找出唯一重复的数

【位运算技巧】异或运算符(2)数组中找出唯一重复的数

前言

位运算 异或(^)蛮有意思的,分享一下。

一、题目

1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。

每个数组元素只能访问一次,设计一个算法,将它找出来。

不用辅助存储空间,能否设计一个算法实现?

提示:异或( ^ )可以去除两个重复的值。

二、题解

1.奇巧淫技

利用异或( ) 可以去重这一特点。a异或a等于0,a异或0等于a(101异或101等于0)例如,A A B C C等于B,因为AA等于0,消除重复了。

解释:

先将 1 -1000的每一个数都 异或( ^ ) 一次。如果此时再将1-1000异或一次,那么值则为0了。

因为在数值中,有1001个数,中间有两个数是相同的。因为第一个for循环 异或 了1-1000,此时再次for循环遍历数组中所有数,同时 异或 所有数,那么重复的那个数字将会被保留下来,所以那个数字即为重复的数。(两个for循环中,重复的数字一共出现了3次,其他数只出现了两次)

使用异或^。时间复杂度为O(n)。

    public static int solution(int[] arr) {
        int x = 0;
        // 先将1-1000异或一次,1^2^3^...^1000。
        for (int i = 1; i <= 1000; i++) {
            x = x ^ i;
        }
        // 再将1-1000异或一次,但是这里包含了一个重复的数(这个重复的数总共会被异或^三次,从而保留下来)。
        for (int i = 0; i < arr.length; i++) { 
            x = x ^ arr[i];
        }
        return x;
    }

测试数据main方法

	public static void main(String[] args) {
        // 随便生成的测试数据(数据可能是乱序的,我这里只是为了方便)
        int[] arr = new int[1001]; 
        for (int i = 0; i < 1000; i++) {
            arr[i] = i + 1;
        }
        arr[1000] = (int)(Math.random() * (1000+1-1))+1; // 随机生成[1-1000]内的数

		// 测试
        System.out.print(solution(arr));
    }

2.其他解法

使用辅助空间

新建了一个HashSet。使用 Set 不能添加重复的特点,如果找到重复的了就返回那个值。

    public static int solution2(int[] arr) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < arr.length; i++) {
            boolean add = set.add(arr[i]);
            if (!add) {
                return arr[i];
            }
        }
        throw new IllegalArgumentException("数组中没有两个相同的数!");
    }

新建了一个数组。因为值的范围在 1-1000,所以用值来作为新数组的下标,将值+1。

第二个for循环遍历,找出被值为2的下标,即重复出现的数。

    public static int solution3(int[] arr) {
        int[] newArr = new int[1001];
        for (int i = 0; i < arr.length; i++) {
            int value = arr[i];
            newArr[value]++;
        }
        for (int i = 0; i < newArr.length; i++) {
            if (newArr[i] == 2) {
                return i;
            }
        }
        throw new IllegalArgumentException("数组中没有两个相同的数!");
    }
不使用辅助空间

暴力破解。时间复杂度有点高,为O(n2)

每找一个数时,判断这个数在数组中是否存在,存在则直接返回。

    public static int solution4(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = i+1; j < arr.length; j++) {
                if (arr[i] == arr[j]) {
                    return arr[i];
                }
            }
        }
        throw new IllegalArgumentException("数组中没有两个相同的数!");
    }