查找数组中出现次数为奇数的两个数

问题:查找数组中出现次数为奇数的两个数,只有两个数出现次数为奇数,其余数字出现次数为偶数。

使用位运算性质:

1、a^a=0

2、-a=~a+1

3、a & (-a)  可以得到a二进制中最右侧1

假设两个奇数分别为a、b

  • 第一次将数组中所有数字异或运算之后得到的结果result=a^b

for (int j : arr) {
    result ^= j;
}

  • 获取result二进制中最右侧1  r,因为result的结果为a^b,根据异或运算的性质可知,a和b只能有一个数字二进制最右侧1  为r,所以可以使用(r & i) != 0或者(r & i) == 0将数组中数字分为两组,一组二进制最右侧1 为r,一组二进制最右侧1 不为r,而且a和b一定不在同一组

   int r = test2(result);

/**
* 获取一个数字最右侧1
* @param n
*/
private static int test2(int n) {
  return n & (-n);
}

  • 将分组后的其中一组进行异或运算得到s,得到a和b其中一个数,即s=a或s=b

int s = 0;

for (int i : arr) {
    // 将数组中最右侧不为1的数^r
    if ((r & i) != 0) {
      s ^= i;
    }
  }

  • 然后使用s^result,即s^a^b,因为异或运算满足交换律,因此,可以得到另一个数,完整代码如下:

/**
* 查找数组中出现次数为奇数的两个数
*
* @param arr
*/
private static List<Integer> test3(int[] arr) {
  int result = 0;
  for (int j : arr) {
    result ^= j;
  }
  int r = test2(result);
  System.out.println(r);
  int s = 0;
  for (int i : arr) {
    // 将数组中最右侧不为1的数^r
    if ((r & i) != 0) {
      s ^= i;
    }
  }

  List<Integer> integers = Arrays.asList(s, s ^ result);
  integers.sort((o1, o2) -> o1 - o2);
  return integers;
}

/**
* 获取一个数字最右侧1
* @param n
*/
private static int test2(int n) {
  return n & (-n);
}

使用hashmap辅助验证:

/**
* hashmap方式
* 查找数组中出现次数为奇数的两个数
*
* @param arr
*/
private static ArrayList<Integer> test3HashMap(int[] arr) {
    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);
        }
    }
    ArrayList<Integer> integers = new ArrayList<>();
    for (int num : map.keySet()) {
        if (map.get(num) % 2 != 0) {
            integers.add(num);
        }
    }
    integers.sort((o1, o2) -> o1 - o2);
    return integers;
}

验证

public static void main(String[] args) {
    int[] arr = randomArray(1000, 2, 20);
    System.out.println(test3(arr));
    System.out.println(test3HashMap(arr));
}

生成数组对数器randomArray:

/**
* 对数器
* @param max 最大值
* @param k 出现次数为奇数的个数
* @param m 出现次数为偶数的个数
* @return
*/
private static int[] randomArray(int max, int k, int m) {
// 记录每个奇数出现的次数
ArrayList<Integer> oddKinds = new ArrayList<>();
// 记录每个偶数出现的次数
ArrayList<Integer> evenKinds = new ArrayList<>();
// 生成出现k个奇数
int valueK;
// 生成每个奇数出现的次数
// 计算数组长度
int len = 0;
while (k != 0) {
do {
valueK = (int) (Math.random() * max + 1);
} while (valueK % 2 == 0);
k--;
len += valueK;
oddKinds.add(valueK);
}
// 生成每个偶数出现的次数
while (m != 0) {
do {
valueK = (int) (Math.random() * max + 1);
} while (valueK % 2 != 0);
m--;
len += valueK;
evenKinds.add(valueK);
}

int[] arr = new int[len];
HashSet<Integer> set = new HashSet<>();
// 添加奇数
int index = 0;
index = addArr(max, oddKinds, arr, set, index);
// 添加偶数
addArr(max, evenKinds, arr, set, index);

// 打乱数组顺序
for (int j = 0; j < arr.length; j++) {
int index1 = (int) (Math.random() * arr.length);
int temp = arr[j];
arr[j] = arr[index1];
arr[index1] = temp;
}

return arr;
}

private static int addArr(int max, ArrayList<Integer> oddKinds, int[] arr, HashSet<Integer> set, int index) {
int valueK;
for (Integer oddKind : oddKinds) {
do {
valueK = randomNumber(max);
} while (set.contains(valueK));
set.add(valueK);
for (int integer = 0; integer < oddKind; integer++) {
arr[index] = valueK;
index++;
}
}
return index;
}

/**
* 生成随机数
* @param range
* @return
*/
private static int randomNumber(int range) {
return ((int) (Math.random() * range) + 1) - ((int) (Math.random() * range) + 1);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

91老码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值