算法数据结构体系学习班 第二节

认识异或运算

异或运算:相同为0,不同为1
同或运算:相同以1,不同为0

能长时间记住的概率接近0%

所以,异或运算就记成无进位相加!

异或运算的性质

1.0^N == N N^N == 0
2.异或运算满足交换律和结合率

上面的两个性质用无进位相加来理解就非常的容易

题目一

如何不用额外变量交换两个数

//如何不用额外变量交换两个数
    public static void main(String[] args) {
        int a = (int) (Math.random() * 10);
        int b = (int) (Math.random() * 10);
        System.out.println(a);
        System.out.println(b);
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println(a);
        System.out.println(b);
    }

题目二

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

//一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
    public static void main(String[] args) {
        int[] a = new int[]{1, 2, 3, 4, 3, 2, 4, 1, 1};
        int jishu = 0;
        for (int i = 0; i < a.length; i++) {
            jishu ^= a[i];
        }
        System.out.println(jishu);
    }

题目三

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

//怎么把一个int类型的数,提取出最右侧的1来
    public static void main(String[] args) {
        int a = (int) (Math.random() * Integer.MAX_VALUE);
        printBinary(a);
        int b = a & (-a);
        printBinary(b);
    }

    public static void printBinary(int a) {
        for (int i = 31; i >= 0; i--) {
            System.out.print(((a >> i) & 1));
        }
        System.out.println();
    }

题目四

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

//一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
    public static void main(String[] args) {
        int[] num = new int[]{1, 2, 3, 4, 3, 2, 4, 1, 1, 2};
        int a = 0;
        for (int i = 0; i < num.length; i++) {
            a ^= num[i];
        }
        System.out.println(a);
        printBinary(a);
        int b = a & (-a);
        System.out.println(b);
        printBinary(b);
        int c = a;
        for (int i = 0; i < num.length; i++) {
            if ((num[i] & b) != 0) {
                a ^= num[i];
            }
        }
        c = c ^ a;
        System.out.println(a);
        System.out.println(c);
    }

    public static void printBinary(int a) {
        for (int i = 31; i >= 0; i--) {
            System.out.print(((a >> i) & 1));
        }
        System.out.println();
    }

题目五

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

/**
     * 一个数组中有一种数出现K次,其他数都出现了M次,
     * M > 1,  K < M
     * 找到,出现了K次的数,
     * 要求,额外空间复杂度O(1),时间复杂度O(N)
     *
     * @param args
     */
    public static void main(String[] args) {
        int kinds = 5;
        int range = 30;
        int testTime = 100000;
        int max = 9;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int a = (int) (Math.random() * max) + 1; // a 1 ~ 9
            int b = (int) (Math.random() * max) + 1; // b 1 ~ 9
            int k = Math.min(a, b);
            int m = Math.max(a, b);
            // k < m
            if (k == m) {
                m++;
            }
            int[] arr = randomArray(kinds, range, k, m);
            int ans1 = test(arr, k, m);
            int ans2 = onlyKTimes(arr, k, m);
            int ans3 = km(arr, k, m);
            if (ans1 != ans2 || ans1 != ans3) {
                System.out.println(ans1);
                System.out.println(ans3);
                System.out.println("出错了!");
            }
        }
        System.out.println("测试结束");
    }

    // 为了测试
    public static int[] randomArray(int maxKinds, int range, int k, int m) {
        int ktimeNum = randomNumber(range);
        // 真命天子出现的次数
        int times = k;
        // 2
        int numKinds = (int) (Math.random() * maxKinds) + 2;
        numKinds--;
        // k * 1 + (numKinds - 1) * m
        int[] arr = new int[times + numKinds * m];
        int index = 0;
        for (; index < times; index++) {
            arr[index] = ktimeNum;
        }

        HashSet<Integer> set = new HashSet<>();
        set.add(ktimeNum);
        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 填好了
        for (int i = 0; i < arr.length; i++) {
            // i 位置的数,我想随机和j位置的数做交换
            int j = (int) (Math.random() * arr.length);// 0 ~ N-1
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        return arr;
    }

    // 为了测试
    // [-range, +range]
    public static int randomNumber(int range) {
        return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1));
    }


    public static int test(int arr[], int k, int m) {
        HashMap<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);
            }
        }
        int ans = 0;
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == k) {
                ans = entry.getKey();
            }
        }
        return ans;
    }


    public static int onlyKTimes(int arr[], int k, int m) {
        int[] a = new int[32];
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < 32; j++) {
                if (((arr[i] >> j) & 1) != 0) {
                    a[j]++;
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < a.length; i++) {
            if (a[i] % m == 0) {
                continue;
            }
            if (a[i] % m == k) {
                ans |= 1<<i;
            }
        }
        return ans;
    }

    // 更简洁的写法
    public static int km(int[] arr, int k, int m) {
        int[] help = new int[32];
        for (int num : arr) {
            for (int i = 0; i < 32; i++) {
                help[i] += (num >> i) & 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < 32; i++) {
            help[i] %= m;
            if (help[i] != 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值