01

题目分析


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


说明

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

示例 1:

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

示例 2:

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

我们拿到题目的一瞬间,用脚趾头都能想到可以通过hash表进行暴力求解。因为题目中已经告知我们除了目标元素之外,其他元素都只出现两次。所以我们可以用一个很简单的逻辑“如果出现第一次就放入map中,如果出现第二次就将其删除”,最终map中剩下的唯一一个元素,就是我们要找的目标元素。该种解法过于简单,直接上代码:

 1func singleNumber(nums []int) int {
 2    m := make(map[int]int)
 3    for _, v := range nums {
 4        if _, exist := m[v]; exist {
 5            //如果元素存在就删除
 6            delete(m, v)
 7        } else {
 8            //如果不存在将其放入,值随意
 9            m[v] = 1
10        }
11    }
12    for k, _ := range m {
13        return k
14    }
15    //在当前题目限制条件下,这一行应该是跑不到的
16    return 0
17}

但是很明显,这种解法并不是我们想要的。因为这种情况下,我们使用到了额外空间。那我们如何在不使用额外空间的前提下,来完成这道题目呢,下面是我们的思考过程。

02

题目图解


首先我们回忆一下,我们知道的按位异或(xor)运算。(这是专门给基础薄弱的道友准备的,懂的可以自行跳过....)


异或(xor)是一个数学运算符,它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。在异或中,如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。(其实很好记忆,就是男的和女的才能生出孩子,如果两个男的或两个女的,那就不行...)

而异或运算,满足于交换律其实也很好理解,男的和女的,女的和男的,其实都可以生出孩子..

注意:这里需要强调的是,异或属于二进制运算。因为真的有人可能会问我"你说a,b不同则结果为1,那为啥我在编译器里计算3^1,最终结果却是2"这样的问题。


在上面的知识基础上,我们只需要将所有数字按照顺序做异或运算,最终剩下的数字就是唯一的数字。


因为任意两个相同的数字进行异或,结果为0

a ^ a = 0

**而0和任意数字进行异或,又等于其本身。**比如 10 ^ 0 ,如图:

03

Go语言示例


分析完毕,则代码自成:

1func singleNumber(nums []int) int {
2    res := 0
3    for _, v := range nums {
4        res ^= v
5    }
6    return res
7}

注:本系列所有教程中都不会用到复杂的语言特性,大家不需要担心没有学过go。算法思想最重要,使用go纯属本人爱好。同时,本系列所有代码均在leetcode上进行过测试运行,保证其严谨性!