题目描述

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

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

示例:

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

输入: [4,1,2,1,2]
输出: 4
^ 按位异或 0 ^ 1 = 1 反之都是0,example 1 ^ 1 = 0
 0011 1100
^0000 1101
-----------------
 0011 0001

按位异或特点:

自己^自己 = 0

 0011 
^0011 
-----
 0000

自己^0 = 自己

 0011 
^0000 
-----
 0011

以[2,2,1]解析

00
10
----
10

10
10
----
00

00
10
----
10

Swift答案示例

func singleNumber(_ nums: [Int]) -> Int {
    var res = 0
    for num in nums {
        res ^= num
    }
    return res
}

原文: https://leetcode-cn.com/problems/single-number/

进阶:

题目描述(难度中等)

有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。

示例:

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

答案

func twoSingleNumber(_ nums: [Int]) -> [Int] {
    // let s = nums.reduce(0,{$0 ^ $1})
    let s = nums.reduce(0, ^)
    let mask = s & (~(s-1))
    var num1 = 0
    var num2 = 0
    
    for n in nums {
        if n & mask == 0 {
            num1 ^= n
        }else {
            num2 ^= n
        }
    }
    return [num1, num2]
}

解析

首先:在这里把所有元素都异或,那么得到的结果就是那两个只出现一次的元素异或的结果

let s = nums.reduce(0, ^) // 0110  6

位运算

 0110 // s=6
-0001 // s-1
------
 0101

// ~二进制补码运算符
// 是一元运算符,具有"翻转"位效果,即0变成1,1变成0
~0101
------
 1010

// &都是1才是1,否则就是0
 0110
&1010
------
 0010

拿到了唯一的掩码 0010

let mask = s & (~(s-1))

二进制

// 0001 = 1
// 0010 = 2
// 0011 = 3
// 0100 = 4
// 0101 = 5

以二进制掩码0010为标准,将数组的 n 个元素分成两部分

for n in nums {
    // 任何数&0010 == 0 或者 == 1
    if n & mask == 0 { // (1, 1, 5)
        num1 ^= n
    }else {// (2, 2, 3)
        num2 ^= n
    }
}

共遍历数组两次,时间复杂度为O(n)