136.只出现一次的数字-Java实现

本文介绍了如何使用位运算高效地找出数组中只出现一次的元素。通过对数组进行异或操作,利用异或运算的性质,可以在线性时间和常数空间复杂度内找到答案。其他解法包括暴力查找、排序、HashSet和HashMap,但它们的时间或空间复杂度不满足题目的要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关标签

  • 位运算
  • 哈希表
     

题目描述

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

 

解法1:暴力查找(双循环,时间复杂度不满足)

class Solution {
    public int singleNumber(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int j;
            // 遍历查找
            for (j = 0; j < nums.length; j++) {
                // 如果下标相等,为同一元素,跳过
                if (i == j){
                    continue;
                }
                // 如果找到相等的元素,进行下次循环
                if (nums[j] == nums[i]){
                    break;
                }
            }
            // 结束单层循环,没有匹配的元素,返回
            if (nums.length == j){
                return nums[i];
            }
        }
        return -1;
    }
}

时间复杂度:O(n^2),空间复杂度:O(1)

  • 执行用时:359ms
     

解法2:排序(快排,时间复杂度不满足)

思路:先排序,排好序的数组元素,两两一组

class Solution {
    public int singleNumber(int[] nums) {
       quickSort(nums,0,nums.length-1);
        for (int i = 0; i < nums.length; i+=2) {
            if (i==nums.length-1){
                return nums[i];
            }
            if (nums[i] != nums[i+1]){
                return nums[i];
            }
        }
        return -1;
    }
    public static void quickSort(int[] arr, int low, int high) {
        int i, j, temp, t;
        if (low > high) {
            return;
        }
        i = low;
        j = high;
        //temp就是基准位
        temp = arr[low];

        while (i < j) {
            //先看右边,依次往左递减
            while (temp <= arr[j] && i < j) {
                j--;
            }
            //再看左边,依次往右递增
            while (temp >= arr[i] && i < j) {
                i++;
            }
            //如果满足条件则交换
            if (i < j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];
        arr[i] = temp;
        //递归调用左半数组
        quickSort(arr, low, j - 1);
        //递归调用右半数组
        quickSort(arr, j + 1, high);
    }
}

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

  • 执行用时:6 ms

 

解法3:HashSet(空间复杂度不满足)

使用集合存储数字。
遍历数组中的每个数字,如果集合中没有该数字,则将该数字加入集合,如果集合中已经有该数字,则将该数字从集合中删除,最后剩下的数字就是只出现一次的数字。

class Solution {
    public int singleNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++){
            if(!set.add(nums[i])){
                set.remove(nums[i]);
            }
        }
        return set.iterator().next();
    }
}

时间复杂度:O(n),空间复杂度:O(n)

  • 执行用时:11 ms

 

解法4:HashMap(空间复杂度不满足)

使用哈希表存储每个数字和该数字出现的次数。
遍历数组即可得到每个数字出现的次数,并更新哈希表,最后遍历哈希表,得到只出现一次的数字。

class Solution {
    public int singleNumber(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        // 遍历数组,存入hashmap
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])){
                map.put(nums[i],1);
            }else {
                map.put(nums[i],2);
            }
        }
        // 遍历hashmap,寻找value为1的key即为所求
        Iterator<Integer> iterator = map.keySet().iterator();
        while (iterator.hasNext()){
            int index = iterator.next();
            if (map.get(index)==1){
                return index;
            }
        }
        return -1;
    }
}

时间复杂度:O(n),空间复杂度:O(n)

  • 执行用时: 18 ms

 

解法5:位运算(√)

如果要求使用线性时间复杂度和常数空间复杂度,上述解法显然都不满足要求。

对于这道题,可使用异或运算。

异或运算有以下三个性质:

  1. 任何数和0做异或运算,结果仍然是原来的数
  2. 任何数和其自身做异或运算,结果是0
  3. 异或运算满足交换律和结合律

(所有元素异或,通过交换律可知,所有两两相同的元素进行异或运算结果都为0,因此最终得到的值即唯一出现的数字)

class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for(int num : nums){
            result ^= num;
        }
        return result;
    }
}

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

  • 执行用时: 1 ms
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值