算法中的异或运算

218 阅读1分钟

异或运算性质

  • 0 ^ N = N
  • N ^ N = 0
  • 满足结合律和交换律,本质是无进位相加

如何不用额外变量交换两个数,使用时要注意什么

//注意i和j不能相同
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];

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

  • 拿0异或,偶数次的数全消,奇数次的数只剩一个
public static void printOddTimesNum1(int[] arr) {
   int eor = 0;
   for (int i = 0; i < arr.length; i++) {
      eor ^= arr[i];
   }
   System.out.println(eor);
}

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

//   011011010000
N & ((~N) + 1)
或者
N & (-N)
//   000000010000

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

public static void printOddTimesNum(int[] arr) {
    int eor = 0;
    // 得到两个奇数次的数的异或结果
    for (int i = 0; i < arr.length; i++) {
        eor = eor ^ arr[i];
    }
    // 通过最右边的1把两个奇数次的数区分开来
    int rightOne = eor & (-eor);
    // 这次只异或和rightOne同为1的数,得到两个奇数次的数其中一个
    int eor1 = 0;
    for (int i = 0; i < arr.length; i++) {
        if((arr[i] & rightOne) != 0) {
            eor1 = eor1 ^ arr[i];
        }
    }
    // eor1 ^ eor 得到另一个
    System.out.println(eor1 +" " + (eor1 ^ eor));
}
public static void main(String[] args) {
    int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 };
    printOddTimesNum(arr2);

}

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

import java.util.HashMap;
import java.util.HashSet;

public class KM {

   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;
   }


   public static int onlyKTimes(int[] arr, int k, int m) {
      int[] help = new int[32];
      // 记录所有数,32位的和
      for (int num : arr) {
         for (int i = 0; i < 32; i++) {
            help[i] += (num >> i) & 1;
         }
      }
      // 如果不能整除m,说明ans的这个位一定是1,因为k < m
      int ans = 0;
      for (int i = 0; i < 32; i++) {
         help[i] %= m;
         if (help[i] != 0) {
            ans |= 1 << i;
         }
      }

      // 如果ans不是k个,返回-1
      int real = 0;
      for (int num : arr) {
         if (num == ans) {
            real++;
         }
      }
      return real == k ? ans : -1;
   }

   // 为了测试
   public static int[] randomArray(int maxKinds, int range, int k, int m) {
      int ktimeNum = randomNumber(range);
      // 可能不出现k次,加一是因为最小为1
      int times = Math.random() < 0.5 ? k : ((int) (Math.random() * (m - 1)) + 1);
      // 加2是因为最少一共有两种
      int numKinds = (int) (Math.random() * maxKinds) + 2;
      // k * 1 + (numKinds - 1) * m
      int[] arr = new int[times + (numKinds - 1) * m];
      int index = 0;
      for (; index < times; index++) {
         arr[index] = ktimeNum;
      }
      numKinds--;
      // set防止随机数字重复
      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;
         }
      }
      // 打乱数组
      for (int i = 0; i < arr.length; i++) {
         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 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);
         if (ans1 != ans2) {
            System.out.println("出错了!");
         }
      }
      System.out.println("测试结束");
   }

}