40_FindNumsAppearOnce

 
 题目描述:
    一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

 思路:
    思路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;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值