题目描述:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路:
思路1:O(n)
首先:位运算中异或的性质:两个相同数字异或(^)为0,一个数和0异或还是它本身。任何数和1与(&)都为任何数的最后一位
1)当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,
因为成对儿出现的都抵消了。
2)依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,
剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。
我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。
如此,两个相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。
3)然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
思路2:O(n^2)
1)快速排序
2)依次计算相同树的个数(麻烦)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public static void main(String[] args) {
Solution s = new Solution();
int[] arr = {2, 4, 3, 6, 3, 2, 5, 5};
int[] num1 = new int[1];
int[] num2 = new int[1];
s.FindNumsAppearOnce(arr, num1, num2);
}
//这个思路好
public void FindNumsAppearOnce(int[] arr, int num1[], int num2[]) {
if (arr == null || arr.length == 0) {
return;
}
// 1.将所有数都异或结果sum,任何数和0异或都为本身
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum ^= arr[i];
}
// 2.从右往左确定sum中第一个为1的位index,任何数和1与都为任何数的最后一位
int index = 0;
for (int i = 0; i < 32; i++) {//int位32位,异或的结果一定是32位
// num << n 表示num左移n位,不分正负数,低位补0
// num >> n 表示右移,如果该数为正,则高位补0,若为负数,则高位补1;此处为补充知识点,
// 参考https://blog.csdn.net/qq_41135254/article/details/97750382
if (((sum >> i) & 1) == 1) {
index = i;
break;
}
}
// 3.将arr分为index为1和index不为1的两组,计算两组的异或结果,得到的就是所需的两数
for (int j = 0; j < arr.length; j++) {
if (((arr[j] >> index) & 1) == 1) {//如果index位为1
num1[0] ^= arr[j];
} else {//如果index不为1
num2[0] ^= arr[j];
}
}
}
//我的方法:不好
//排序后,再依次计算每个数的个数,为1时加入结果集中。我在排序后搞了很久,代码能力不行
public void FindNumsAppearOnce2(int[] array, int num1[], int num2[]) {
quickSort(array, 0, array.length - 1);
int count = 0;//从0位置开始,所以计数器初始为0
int tmp = array[0];
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
//当前数tmp与记录的数相同时,计数器+1;否则判定计数器是否为1,
// 为1时加入结果集合(加入的是前一个数,再重新记录tmp和重置计数器
if (array[i] == tmp) {
count++;
} else {
if (count == 1) {//这里是将前一个数加入list中,不是目前判断的数,此时tmp没有重置
list.add(new Integer(tmp));
}
tmp = array[i];
count = 1;//重置为1
}
//注意,判断到最后一个数时,无论是否与前面相同,都不会被加入集合中,所以要单独判断
if (i == array.length - 1) {
if (count == 1) {
list.add(new Integer(tmp));
}
}
}
num1[0] = list.get(0);
num2[0] = list.get(1);
}
public void quickSort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int l = left;
int r = right;
int base = arr[left];
while (l < r) {
//从后往前找比base小的
while (r > l && arr[r] >= base) {
r--;
}
//从前往后找大的
while (r > l && arr[l] <= base) {
l++;
}
//交换
swap(arr, l, r);
}
//base和最后的left交换
swap(arr, left, l);
quickSort(arr, left, l - 1);
quickSort(arr, l + 1, right);
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}