算法之异或相关题目

算法之异或

  • 0 ^ N = 0
  • N ^ N = 0
  • 1 ^ N = 1
  • (a ^ b) ^ c = a ^ (b ^ c)
  • a ^ b = b ^ a

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

    /**
     * i和j不能为同一个位置,不然为全0
     * @param arr
     * @param i
     * @param j
     */
    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];
    }

2、一个数组中一个数出现了奇数次,剩下数都出现了偶数次,找到并打印

    /**
     * 找出数组中出现奇数次的数
     * @param arr
     * @return
     */
    public static int findOdd(int[] arr){
        int res = 0;
        for(int i = 0;i < arr.length;i++){
            res = res ^ arr[i];
        }
        return res;
    }

3、提取一个整形最右边的1

    /**
     * 取反加1再与上自身
     * @param num
     * @return
     */
    public static int find(int num){
        return num & (~num + 1);
    }

4、一个数组中两种数字出现了奇数次,其余都出现了偶数次,找到并打印这个数

  • 三次异或
    1. 总体异或一次
    2. 异或对应位为1的数,即可解出其中一个数
    3. 前两次答案异或,得出另一个数
  public static int[] printOddTimesNum2(int[] arr){
        int eor = 0;
        for(int i = 0;i < arr.length;i ++){
            eor ^= arr[i];
        }

        //找出eor最右侧的1
        //将所有数分为 对应位 为1 和不为1 的两类
        int rightOne = eor & (~eor + 1);

        //eor' 
        int onlyOne = 0;
        for(int i = 0;i < arr.length;i++){
            if((rightOne & arr[i]) != 0){
                onlyOne = arr[i] ^ onlyOne;
            }
        }

        eor = eor ^ onlyOne;
        return new int[]{eor,onlyOne};
    }

5、一个数组中有一个数出现k次,其他数出现了M次(K < M,M > 1,O(N))

  • 一个数出现了K次,另一个数字出现了M次,且K<M,M > 1;并且时间复杂度为O(1)
  • 可以改进一个数出现k次,一个数不出现k次,其余m次。可以在模m后再模k不为0得出 -> 不出现k次的元素
package class_01.xor;

import javax.swing.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class Code_KM {

    /**
     * 使用传统哈希表,建立表项
     * @param arr
     * @param k
     * @param m
     * @return
     */
    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;
    }

    /**
     * arr中一个数出现k次,其余所有数字都出现m次
     * @param arr
     * @param k
     * @param m
     * @return
     */
    public static int onlyKTimes(int[] arr,int k,int m){
        int[] t = new int[32];
        for(int i = 0;i < arr.length;i++){
            for(int j = 0;j <= 31;j++){
                t[j] += ((arr[i] >> j) & 1);
            }
        }

        int res = 0;
        for (int i = 0; i < 32; i++){
            if((t[i] % m) != 0){
                res |=  (1 << i);
            }
        }
        
        //如果恰好0出现了k次,需要再进行检查
        if(res == 0){
            int count = 0;
            for(int num : arr){
                if(num == 0){
                    count++;
                }
            }
            if(count != k){
                return -1;
            }
        }
        return res;
    }

    public static int[] randArray(int maxKinds,int range,int k,int m){
        //出现k次的数
        int ktimeNum = (int) (Math.random() * maxKinds) + 1;
        //共有多少种数
        int numKinds = (int)(Math.random() * maxKinds) + 2;
        int[] arr = new int[k + (numKinds - 1) * m];
        int index = 0;
        for(;index < k;index++){
            arr[index] = ktimeNum;
        }
        numKinds--;
        HashSet<Integer> set = new HashSet<>();
        set.add(ktimeNum);
        while (numKinds != 0){
            int curNum = 0;
            do {
                curNum = (int) (Math.random() * range) + 1;
            }while (set.contains(curNum));
            set.add(curNum);
            numKinds--;
            for(int i = 0;i < m;i++){
                arr[index++] = curNum;
            }
        }

        for(int i = 0;i < arr.length;i++){
            //随机交换元素
            int j = (int) (Math.random() * arr.length);
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        return arr;
    }

    public static void main(String[] args) {
        int kinds = 4;
        int range = 200;
        int max = 9;
        int testTimes = 100000;
        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;
            int k = Math.min(a,b);
            int m = Math.max(a,b);
            if(k == m){
                m++;
            }
            int[] arr = randArray(kinds,range,k,m);
            int ans1 = test(arr,k,m);
            int ans2 = onlyKTimes(arr,k,m);
            if(ans1 != ans2){
                System.out.println("出错了.....");
            }
        }
        System.out.println("测试结束...");
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值