第2节.pptx

1 异或运算的应用

题目1:不用额外变量交换2个数

02 异或运算 - 图1

 /**
     * 如何不用额外变量交换两个数
     * @param a
     * @param b
     */
    public static void swap(int a, int b) {
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
    }

题目2:一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数

02 异或运算 - 图2

 /**
     * 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
     *
     * @param arr
     * @return
     */
    public static Integer findOddTimesNum(int[] arr) {
        if (arr == null || arr.length < 1) {
            return null;
        }

        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        return eor;
    }


    /*
     * 以下为对数器,用于测试
     * */

    public static Integer comparator(int[] arr) {
        if (arr == null || arr.length < 1) {
            return null;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            if (map.containsKey(arr[i])) {
                map.put(arr[i], map.get(arr[i]) + 1);
            } else {
                map.put(arr[i], 1);
            }
        }

        Set<Integer> keySet = map.keySet();
        for (Integer key : keySet) {
            if ((map.get(key) & 1) == 1) {
                return key;
            }
        }
        return null;
    }

    /**
    * 生成符合要求的测试数据,一个数出现奇数次,其余出现偶数次
    */
    public static int[] generateEvenAndOddArr(int maxTimes, int numKinds, int maxValue) {

        int times = (int) (Math.random() * maxTimes + 1);
        //1个数出现的奇数次
        int oddTimes = (times & 1) == 0 ? times - 1 : times;
        //剩下的偶数次数的种数
        int[] evenTimes = new int[numKinds - 1];

        //生成数组的长度
        int length = oddTimes;

        //出现偶数次的每种数,出现的偶数次
        for (int i = 0; i < evenTimes.length; i++) {
            do {
                evenTimes[i] = (int) (Math.random() * maxTimes + 1);
            } while ((evenTimes[i] & 1) == 1);
            length += evenTimes[i];
        }
        int[] ans = new int[length];
        //出现奇数次的数
        int oddNum = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1));
        // 先把生成的出现奇数次的数放入ans数组

        int i = 0;
        while (i < oddTimes) {
            ans[i++] = oddNum;
        }

        //把出现偶数次的数放入ans数组
        for (int i1 = 0; i1 < evenTimes.length; i1++) {
            int evenNum = oddNum + (int) (Math.random() * maxValue + 1);
            for (int j = i; j < i + evenTimes[i1]; j++) {
                ans[j] = evenNum;
            }
            i = i + evenTimes[i1];
        }

        for (int j = 0; j < length; j++) {
            int temp = ans[j];
            int index = (int) (Math.random() * length);
            ans[j] = ans[index];
            ans[index] = temp;
        }

        return ans;
    }

    public static void prinArr(int[] arr) {
        if (arr == null || arr.length < 1) {
            return;
        }
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }

题目三:怎么把一个int类型的数,提取出最右侧的1来

02 异或运算 - 图3

     /**
     * 一个int类型的数,提取出二进制最右侧的1
     *
     * @param num
     */
    public static int nearRightOne(int num) {
        return num & (~num + 1);
    }

    /**
     * 打印一个数的二进制位信息
     *
     * @param num
     */
    public static void printByte(int num) {
        for (int i = 31; i >= 0; i--) {
            System.out.print((num >> i) & 1);
        }
    }

题目4:一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数

02 异或运算 - 图4

    /**
     * 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
     *
     * @param arr
     */
    public static List<Integer> evenTimesOddTimes2(int[] arr) {
        if (arr == null || arr.length < 2) {
            return null;
        }
        //假设2个奇数次的数为a和b,eor =a^b
        int eor = 0;
        for (int i : arr) {
            eor ^= i;
        }
        //a!=b,则eor>0,eor1为eor二进制最右侧的1
        int eor1 = eor & (~eor + 1);
        int ans1 = 0;
        for (int i : arr) {
            if ((i & eor1) == 0) {
                ans1 = ans1 ^ i;
            }
        }
        List<Integer> ans = new ArrayList<>(2);
        ans.add(ans1);
        ans.add(ans1 ^ eor);
        return ans;
    }

题目5:一个数组中有一种数出现K次,其他数都出现了M次,M > 1, K < M找到,出现了K次的数,要求,额外空间复杂度O(1),时间复杂度O(N)

02 异或运算 - 图5

    /**
     * 一个数组中有一种数出现K次,其他数都出现了M次,
     * M > 1,  K < M
     * 找到,出现了K次的数,
     * 要求,额外空间复杂度O(1),时间复杂度O(N)
     *
     * @param arr
     * @return
     */
    public static Integer kTimesNum(int[] arr, int k, int m) {
        if (arr == null || arr.length < 3) {
            return null;
        }

        int length = 32;
        int[] bytes = new int[length];
        for (int i = 0; i < length; i++) {
            for (int num : arr) {
                bytes[i] += (num >> i) & 1;
            }
        }

        int ans = 0;
        for (int i = 0; i < length; i++) {
            if ((bytes[i] % m) == k) {
                ans |= (1 << i);
            }
        }
        return ans;
    }