数组中只出现1次的数字

1.题目

在这里插入图片描述

2.解法1(归并排序+遍历)

时间复杂度O(nlogn) + O(n),空间复杂度O(1)

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        // 使用的是归并排序,时间复杂度O(nlogn)
        Arrays.sort(array);

        int number = 0;
        int count = 0;
        List<Integer> list = new ArrayList<>();
        
        // 遍历数组,时间复杂度O(n),所以总时间复杂度为O(nlogn)
        for (int i = 0; i < array.length; ++i) {
            if (count == 0) {
                number = array[i];
            }

            if (array[i] == number) {
                ++count;
                if (count == 2) {
                count = 0;
                }
            }
            
            // 用或的条件解决问题(最后一个数字正好只出现1次)
            if (count == 1 && (i + 1 == array.length || number != array[i + 1])) {
                list.add(number);
                count = 0;
            }
        }
        num1[0] = list.get(0);
        num2[0] = list.get(1);
    }
}

2.解法2(异或)Best

public class Solution {
    public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        // 如果数组为null,或者数组中没有2个树,就返回
        int len = array.length;
        if(array == null || len < 2){
            return;
        }

        // 求所有数组数字整体的异或值(相同为0,不同为1),相同的两个数将被抵消,只留下不同两个数的异或值
        int exclusizeOR = 0;
        for (int i = 0; i < len; i++) {
            exclusizeOR ^= array[i];
        }

        /**以异或结果倒数出现的第一个1的位置为准,看数组每个数字二进制的此位置是不是为1,如果是1,为一组
         * 如果是0为另外一组,将数组分为两个子数组 (拆分)
         * 两个相同的数字,肯定会被分到同一组,因为该位置值相同
         * 两个不同的数肯定被分到不同组,因为只有该位置两个值不同,结果才会为1
         * 这样的话,就可以将题目转换为一个数组中只有1个数字只出现1次,其他数字出现2次,整个数组取异或的结果
         * 肯定就是这个只出现1次的数字了
        */

        int moveCount = getLastIndex(exclusizeOR);

        for (int i = 0; i < len; i++) {
            if(isBit1(array[i], moveCount) == 1){
                num1[0] ^= array[i];
            }else{
                num2[0] ^= array[i];
            }
        }
    }

    public int getLastIndex(int num){
        /**判断异或结果中倒数出现第一个1的位置(为什么是倒数第一个,因为这样可以和1取&,而不是最高位为1的32位的二进制数)
        * 思路是将异或结果向右移动,直到和1取&,结果为1,记录移动的次数,就是倒数第一个位置
         */
        int moveCount = 0;
        // java移位操作都是int,所以最多移动32位
        while((num & 1) == 0 && moveCount < 32){
            num = num >> 1;
            ++moveCount;
        }
        return moveCount;
    }

    // 判断数字该位置是不是为1
    public int isBit1(int num, int moveCount){
        num = num >> moveCount;
        return num & 1;
    }


    public static void main(String[] args) {
        Solution s = new Solution();
        int[] num1 = new int[1];
        int[] num2 = new int[1];
        s.FindNumsAppearOnce(new int[]{2, 4, 3, 6, 3, 2, 5, 5}, num1, num2);
        System.out.println(num1[0]);
        System.out.println(num2[0]);
    }
}
时间复杂度为O(n), 空间复杂度为O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值