首页 > 其他分享 >2、异或运算

2、异或运算

时间:2023-02-20 17:47:57浏览次数:34  
标签:arr 运算 int range eor 异或 num println

1、按位异或运算 ^

按位异或(^): 相同为0,不同为1;
按位异或(^): 相当于无进位相加
例如:01010 ^ 01101 = 00111
特性:

  • 任何数异或上0等于其本身: 0 ^ n = n
  • 两个相同的数异或为0: n ^ n = 0
  • 异或运算满足交换律,和结合律
    • 交换:a ^ b = b ^ a
    • 结合:(a ^ b) ^ c = a ^ (b ^ c)

3、常见 Coding

3.1、不使用额外变量交换两个数

注意:a,b 不能是同一个块内存,a,b不能指向同一个内存
示例:
a = 5,b = 6
a = 6,b = 5
步骤:

  • a = a ^ b
  • b = a ^ b,相当于 b = (a ^ b) ^ b = a
  • a = a ^ b,相当于 a = (a ^ b) ^ a = b

代码实现:

点击查看代码
public class XorSwap {
    public static void swap(int[] arr, int i, int j) {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

    public static void main(String[] args) {
        int a = 5;
        int b = 6;
        System.out.println("a=" + a + ", b=" + b);

        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println("a=" + a + ", b=" + b);

        int[] arr = {3, 1, 100};
        int i = 0;
        int j = 0;

        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];

        System.out.println("arr[i]=" + arr[i] + ", arr[j]=" + arr[j]);
        // [0, 1, 100]
        System.out.println(Arrays.toString(arr));
    }
}

3.2、怎么把一个int类型的数,提取出最右侧的1来

操作: a 按位与 a 的相反数,a & (-a) = a & (~a + 1)
例:

a: 0010 1100
~a:1101 0011 // 取反
+1:1101 0100 // 取反加一,即 -a
a & (-a) = 0000 0100

3.3、数组arr[]中一种数出现了奇数次,其他都出现了偶数次,怎么找到并打印这种数?

分析: 异或相当于无进位相加,偶数个 1 相加为 0,奇数个 1 相加为 1

    public static void printOddTimesNum(int[] arr) {
        int eor = 0;
        int len = arr.length;
        for (int i = 0; i < len; i++) {
            eor ^= arr[i];
        }
        System.out.println(eor);
    }

3.4、数组arr[]中,有两种数,出现奇数次,其他出现偶数次,怎么找到并打印这两种数?

3.4.1、分析

  • 假设 a,b出现奇数次,[..a,..,b..]
  • 遍历异或arr 中所有数得到结果一定是: eor = a ^ b
  • 将 eor最右侧的 1提取出来,则可以将arr 分为两堆数据 [{a...},{b...}]
  • 遍历异或{a...} 这一堆数据结果即为: eor' = a
  • 所以 b = eor ^ eor' = a ^ b ^ a

3.4.2、代码实现

点击查看代码
public class EvenTimesOddTimes {
    /**
     * arr中,有两种数,出现奇数次,其他出现偶数次
     * 步骤:假设 a,b出现奇数次
     * 遍历异或所有数,得到结果 eor = a ^ b != 0
     *
     * @param arr
     */
    public static void printOddTimesNum2(int[] arr) {
        int eor = 0;
        int len = arr.length;
        for (int i = 0; i < len; i++) {
            eor ^= arr[i];
        }

        // 将eor 最右侧的 1 提取出来
        // 例: eor:  0011 0100
        // rightOne: 0000 0100
        int rightOne = eor & (-eor);

        // eor = a ^ b,则最右侧 1提取的数 rightOne, 就可以把 arr 分为两堆 [{a...},{b...}]
        // ero', 假设 a 的最右侧 1,与 rightOne 相等,则用 eor' 异或 {a...} 这一堆数其结果就是 eor' = a
        int eor_ = 0;
        for (int i = 0; i < len; i++) {
            if ((rightOne & arr[i]) != 0) {
                eor_ ^= arr[i];
            }
        }
        // eor = a ^ b,eor' = a,则 b = eor ^ eor'
        System.out.println("a=" + eor_ + " b=" + (eor ^ eor_));
    }

    public static void main(String[] args) {
        int[] arr = {3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1};
        printOddTimesNum(arr); // 2

        int[] arr1 = {3, 3, 2, 3, 1, 1, 5, 1, 3, 1, 1, 1};
        printOddTimesNum2(arr1); // a=5 b=2
    }
}

3.5、数组中所有的数都出现了M次,只有一种数出现了K次,返回出现了K次的数

  • 输入 arr[] 一定能保证
  • 数组中所有的数都出现了 M 次,只有一种数出现了 K 次
  • 且 1 <= K < M
  • 返回:出现了 K 次的数
  • 要求:额外空间复杂度 O(1),时间复杂度 O(N)

3.5.1、分析

  • int 类型的数 4 字节 = 32 位,申请一个临时数组 temp[32]
  • 将所有数的二进制位,出现 1 的次数保存到temp 中
    • temp[0] 0位置的 1 出现了几次
    • temp[i] i位置的 1 出现了几次
  • 因为 K < M,所以如果某一位的 1 出现的次数不是 M 的整数倍,那么出现 K 次的数在该位置上一定为 1

3.5.2、代码实现,三种不同的实现方式

点击查看代码
/**
 * 输入 arr[] 一定能保证
 * 数组中所有的数都出现了 M 次,只有一种数出现了 K 次
 * 且 1 <= K < M
 * 返回:出现了 K 次的数
 * 要求:额外空间复杂度 O(1),时间复杂度 O(N)
 *
 * @author smile
 */
public class KM {

    /**
     * 数组中所有的数都出现了 M 次,只有一种数出现了 K 次
     *
     * @param arr 数组
     * @param K   某个数出现了 k 次
     * @param M   出现了 m 次的数
     * @return 出现了 k 次的数
     */
    public static int onlyKTimes(int[] arr, int K, int M) {
        int[] temp = new int[32];

        // temp[0] 0位置的 1 出现了几次
        // temp[1] 1位置的 1 出现了几次
        for (int num : arr) {
            for (int i = 0; i <= 31; i++) {
                temp[i] += (num >> i) & 1;
            }
        }

        // K < M,如果某一位的 1 出现的次数不是 M 的整数倍,
        // 那么出现 K 次的数在该位置上一定为 1
        int res = 0;
        for (int i = 0; i < 32; i++) {
            if (temp[i] % M != 0) {
                res |= (1 << i);
            }
        }
        return res;
    }

    public static HashMap<Integer, Integer> maps = new HashMap<>();

    public static int onlyKtimes1(int[] arr, int k, int m) {
        if (maps.size() == 0) {
            mapCreate(maps);
        }

        int[] temp = new int[32];
        // temp[0] 0 位置的 1 出现了几次
        // temp[i] i 位置的 1 出现了几次
        for (int num : arr) {
            while (num != 0) {
                int rightOne = num & (-num);
                temp[maps.get(rightOne)]++;
                // 将 num 的最后一位 1 置 0
                num ^= rightOne;
            }
        }

        int res = 0;
        for (int i = 0; i < 32; i++) {
            if (temp[i] % m != 0) {
                res |= (1 << i);
            }
        }
        return res;
    }

    public static void mapCreate(HashMap<Integer, Integer> maps) {
        int value = 1;
        for (int i = 0; i < 32; i++) {
            maps.put(value, i);
            value <<= 1;
        }
    }

    // 测试,哈希表统计的形式
    public static int test(int[] arr, int K, int M) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : arr) {
            if (map.containsKey(num)) {
                map.put(num, map.get(num) + 1);
            } else {
                map.put(num, 1);
            }
        }

        for (int num : map.keySet()) {
            if (map.get(num) == K) {
                return num;
            }
        }
        return -1;
    }

    /**
     * 生成满足条件,所有的数都出现了 M 次,只有一种数出现了 K 次,的数组
     *
     * @param maxKinds 数组中最多有几种数
     * @param range    数组中每个数的范围 [-range, range]
     * @param k        出现了 k 次
     * @param m        出现了 m 次
     * @return 新数组
     */
    public static int[] generateRandomArray(int maxKinds, int range, int k, int m) {
        // 先生成出现了 k 次的数
        int kTimesNum = randomNumber(range);
        int times = k;

        // 数的种类,最少 2 种数
        int numKinds = (int) (Math.random() * maxKinds) + 2;

        // 数组长度: k * 1 + (numKinds - 1) * m
        int[] arr = new int[k + (numKinds - 1) * m];
        int index = 0;
        for (; index < times; index++) {
            arr[index] = kTimesNum;
        }

        // k 次的数加入之后,种类减 1
        numKinds--;
        HashSet<Integer> set = new HashSet<>();
        set.add(kTimesNum);

        // 设置出现 m 次的数
        while (numKinds != 0) {
            int curNum = 0;
            do {
                curNum = randomNumber(range);
            } while (set.contains(curNum));
            set.add(curNum);
            numKinds--;
            for (int i = 0; i < m; i++) {
                arr[index++] = curNum;
            }
        }

        // arr 已经生成,将其打乱
        int len = arr.length;
        for (int i = 0; i < len; i++) {
            // i 位置的数,随机和 j 位置的数交换
            // 0 ~ N - 1
            int j = (int) (Math.random() * len);
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        return arr;
    }

    /**
     * 生成 [-range, range] 范围的数
     *
     * @param range 整数
     * @return 整数
     */
    private static int randomNumber(int range) {
        return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1));
    }


    public static void main(String[] args) {
        int[] arr = {1, 2, 2, 4, 2, 1, 1, 4};
        int k = 2;
        int m = 3;
        // 4
        System.out.println(onlyKTimes(arr, k, m));

        int[] arr1 = {1, 2, 2, -1, 2, 1, 1, -1};
        // -1
        System.out.println(onlyKTimes(arr1, k, m));

        // 测试 KM
        int kinds = 5;
        int range = 30;
        int testTimes = 500;
        int max = 9;
        System.out.println("测试开始");
        for (int i = 0; i < testTimes; i++) {
            // 1 ~ 9
            int a = (int) (Math.random() * max) + 1;
            int b = (int) (Math.random() * max) + 1;
            k = Math.min(a, b);
            m = Math.max(a, b);
            // 保证 k < m
            if (k == m) {
                m++;
            }

            int[] array = generateRandomArray(kinds, range, k, m);
            int res1 = test(array, k, m);
            int res2 = onlyKTimes(array, k, m);
            int res3 = onlyKtimes1(array, k, m);

            if (res1 != res2 || res2 != res3) {
                System.out.println("出错了!");
                System.out.println(res1);
                System.out.println(res2);
                System.out.println(res3);
                break;
            }
        }
        System.out.println("测试结束!");
    }
}

标签:arr,运算,int,range,eor,异或,num,println
From: https://www.cnblogs.com/kaibo-li/p/16954179.html

相关文章

  • 7、位运算,位图
    1、位运算顾名思义,位运算符作用于位,是逐位进行操作。最常用的有:与&、或|、异或^。对于基本的位运算,我们有一个常用的口诀:1.按位与&:遇0则02.按位或|:遇1则13.按位......
  • 【图像处理】-形态学调整(膨胀、腐蚀、开/闭运算)
    形态学调整膨胀操作主要针对卷积计算区域内的该像素点的NxM区域的最大值进行膨胀操作;例如:前景是白色背景经过膨胀后区域会增大原图片(背景为黑色,前景为白色)如下:注:图......
  • 一元运算符 java 230220
    自增++inta=1;b=a++;自减--inta=1;b=--a;......
  • 位运算与二进制表示集合
    位运算与二进制表示集合位运算运算符运算运算符数学符号表示解释与&&、\(and\)只有两个对应位都为\(1\)时才为\(1\)或\(|\)\(|\)、\(or\)只要两......
  • 三元运算符
    三元运算符三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(not)的条件判断,在Python2.4以上才有了三元操作。下面是一个伪代码和例子:伪代码:#如......
  • 与用户交互、格式化输出、基本运算符
    一、程序与用户交互1.1什么是与用户交互用户交互就是人往计算机中input/输入数据,计算机print/输出结果1.2为什么要与用户交互为了让计算机能够像人一样与用户沟通交流......
  • 为笛卡尔积运算而生的Reduce(Excel函数集团)
    我要是没记错,Reduce这词是减少的意思,可是当他作为Excel函数出现时,我真没看出哪里Reduce了……好吧,其实可以换种理解,缩减了嵌套(帮助里写的是“将数组缩减为累计值)。来来来......
  • 【Java-01-2】java基础-基本语法(2)(关系运算,if,循环)
    1、关系/逻辑/条件运算符,if语句/**关系运算,if,循环*条件:condition*注意逻辑运算符的短路特性*/importjava.io.*;publicclass_05_Realtional{publi......
  • 05-python运算符
    运算符算术运算符算数运算符:+-*///%**#+var1=7var2=90res=var1+var2print(res)#97#-var1=7var2=90res=var1-var2print(res)#......
  • 【系统架构设计师】计算机组成与体系结构 ① ( 计算机组成 | CPU | 存储器 | 总线 | I
    文章目录​​一、计算机组成与体系结构​​​​二、计算机组成结构​​​​三、CPU组成​​​​1、运算器​​​​2、控制器​​一、计算机组成与体系结构计算机组成与体......