[LeetCode] 136.只出现一次的数字

一、问题概述

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

说明:

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

示例 1:

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

示例 2:

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

链接:https://leetcode-cn.com/problems/single-number/

二、问题分析

如果不对此题不加时空限制的话,可以想出一些解法,例如:

  1. 二重循环暴力查找

    每次选取数组中一个元素,再遍历整个数组看是否能找到相同的。若找不到则该元素出现一次。

    这种方法最容易想到,但在最坏的情况下会遍历\(n^2\)次,时间复杂度为\(O(n^2)\)

  2. 使用hash表记录每个元素出现次数

    创建一个hash表,遍历一遍数组的同时将每个元素出现的次数记录在hash表中,之后遍历hash表从中找出出现次数为1的元素。

    这种方法可以做到线性时间复杂度,但使用了额外的空间。

  3. 排序数组再遍历

    先将数组排序,之后遍历数组,判断每个元素与相邻的元素是否相同。若该元素与左右相邻元素都不同,则此元素只出现一次。

    这种方法也是我第一反应想到的,虽然没有使用额外的空间,但使用快排的话,时间复杂度为\(O(nlogn)\)。此外对边界条件的判断我也不是很熟练,因此代码写得也很臃肿。

参考了大佬们的题解后,发现我漏了一个很重要的条件,就是每个元素最多重复两次!因此就可以根据异或(XOR)运算的性质来巧妙地解这道题。

首先介绍一下异或运算有以下性质:

  • 任何数和 0 做异或运算,结果仍然是原来的数,即 \(a\oplus0=a\)
  • 任何数和其自身做异或运算,结果是 0,即 \(a \oplus a=0\)
  • 异或运算满足交换律和结合律,即 \(a \oplus b \oplus a=b \oplus a \oplus a=b \oplus (a \oplus a)=b \oplus0=b\)

基于此性质,我们就可以遍历一遍数组,将所有元素逐个进行异或运算。

并根据交换律和结合律,将重复的元素两两结合,经异或运算后得到0,最后就变成:

\[0\oplus0\oplus0\oplus ... \oplus0\oplus a=a \]

因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。

三、涉及算法

  1. 异或(XOR)运算

四、AC代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        for (int i = 1; i < nums.size(); i++)
        {
            nums[0] ^= nums[i];
        }
        return nums[0];
    }
};

五、复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)

posted @ 2020-08-03 23:39  hanbin92381  阅读(64)  评论(0编辑  收藏  举报