点击上方“五分钟学算法”,选择“星标”公众号web
重磅干货,第一时间送达算法
今天咱们来作几道很是经典的题目,第一道题目咱们会用多种方法解答,虽然这是一道简单题目,可是咱们学会了这几种解题方法,彻底能够轻松应对后面两道中等题目。废话很少说,咱们来看题目吧。
数组
为保证严谨性,文章中的全部代码均通过测试,你们能够放心食用 题目来源:leetcode 136只出现一次的数(简单),137只出现一次的数Ⅱ(中等) 260只出现一次的数Ⅲ(中等)
只出现一次的数
给定一个非空整数数组,除了某个元素只出现一次之外,其他每一个元素均出现两次。找出那个只出现了一次的元素。微信
示例 1:app
输入: [2,2,1] 输出: 1编辑器
示例 2:测试
输入: [4,1,2,1,2] 输出: 4flex
这个题目很是容易理解,就是让咱们找出那个只出现一次的数字,那么下面咱们来看一下这几种解题方法吧url
HashMap
用 HashMap 的这个方法是很容易实现的,题目要求不是让咱们求次数嘛,那咱们直接遍历数组将每一个数字和其出现的次数存到哈希表里就能够了,而后咱们再从哈希表里找出出现一次的那个数返回便可。spa

题目代码
排序搜索法
这个方法也是特别容易想到的,咱们首先对数组进行排序,而后遍历数组,由于数组中其余数字都出现两次,只有目标值出现一次,因此则让咱们的指针每次跳两步,当发现当前值和前一位不同的状况时,返回前一位便可,固然咱们须要考虑这种状况,当咱们的目标值出如今数组最后一位的状况,因此当数组遍历结束后没有返回值,则咱们须要返回数组最后一位,下面咱们看一下动图解析。

题目代码
这个方法也是比较容易实现的,咱们利用 HashSet 来完成。HashSet 在咱们刷题时出现频率是特别高的,它是基于 HashMap 来实现的,是一个不容许有重复元素的集合。那么在这个题解中,它起到什么做用呢?

题目代码
栈
该方法也很容易想到,咱们首先将其排序,而后遍历数组,若是栈为空则将当前元素压入栈,若是栈不为空,若当前元素和栈顶元素相同则出栈,继续遍历下一元素,若是当前元素和栈顶元素不一样的话,则说明栈顶元素是只出现一次的元素,咱们将其返回便可。这个题目也可使用队列作,思路一致,咱们就不在这里说明啦。下面咱们看下动图解析。

题目代码
求和法
这个方法也比较简单,也是借助我们的 HashSet 具体思路以下,咱们经过 HashSet 保存数组内的元素,而后进行求和(setsum),那么获得的这个和则为去除掉重复元素的和,咱们也能够获得全部元素和(numsum)。由于咱们其余元素都出现两次,仅有一个元素出现一次,那咱们经过 setsum * 2 - numsum 获得的元素则为出现一次的数。

上面咱们的 SetSum * 2 - NumSum = z 也就是咱们所求的值, 是否是感受很简单呀。
题目代码
位运算
这个方法主要是借助我们的位运算符 ^ 按位异或,咱们先来了解一下这个位运算符。
按位异或(XOR)运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两数对应的二进位相异时,结果为1。相同时为0。
任何数和0异或,仍为自己:a⊕0 = a 任何数和自己异或,为0:a⊕a = 0 异或运算知足交换律和结合律:a⊕b⊕a = (a⊕a)⊕b = 0⊕b = b
例:

咱们经过上面的例子了解了异或运算,对应位相异时得 1,相同时得 0,那么某个数跟自己异或时,由于对应位都相同因此结果为 0 , 而后异或又知足交换律和结合律。则

题目代码
本题一共介绍了6种解题方法,确定还有别的方法,欢迎你们讨论。你们能够在作题的时候一题多解。这样能大大提升本身解题能力。下面咱们来看一下这些方法如何应用到其余题目上。
只出现一次的数Ⅱ
给定一个非空整数数组,除了某个元素只出现一次之外,其他每一个元素均出现了三次。找出那个只出现了一次的元素。
示例 1:
输入: [2,2,3,2] 输出: 3
示例 2:
输入: [0,1,0,1,0,1,99] 输出: 99
题目很容易理解,刚才的题目是其余元素出现两次,目标元素出现一次,该题是其余元素出现三次,目标元素出现一次,因此咱们彻底能够借助上题的一些作法解决该题。
求和法
咱们在上题中介绍了求和法的解题步骤,如今该题中其余元素都出现三次,咱们的目标元素出现一次,因此咱们利用求和法也是彻底 OK 的。下面咱们来看具体步骤吧。
1.经过遍历数组获取全部元素的和以及 HashSet 内元素的和。
2.(SumSet * 3 - SumNum)/ 2便可,除以 2 是由于咱们减去以后获得的是 2 倍的目标元素。
注:这个题目中须要注意溢出的状况 。
题目代码
这个题目用 HashMap 和排序查找确定也是能够的,你们能够本身写一下,另外咱们在第一题中有个利用异或求解的方法,可是这个题目是出现三次,咱们则不能利用直接异或来求解,那还有其余方法吗?
位运算
这个方法主要作法是将咱们的数的二进制位每一位相加,而后对其每一位的和取余 ,咱们看下面的例子。

那么咱们为何要这样作呢?你们想一下,若是其余数都出现 3 次,只有目标数出现 1 次,那么每一位的 1 的个数无非有这2种状况,为 3 的倍数(全为出现三次的数) 或 3 的倍数 +1(包含出现一次的数)。这个 3 的倍数 +1 的状况也就是咱们的目标数的那一位。
题目代码
咱们来解析一下咱们的代码
<< 二进制左移运算符。左操做数的值向左移动右操做数指定的位数。 >> 二进制右移运算符。左操做数的值向右移动右操做数指定的位数。
另外咱们的代码中还包含了 a & 1 和 a | 1 这有什么做用呢?继续看下图
& 按位与运算符:参与运算的两个值,若是两个相应位都为1,则该位的结果为1,不然为0

由于咱们 a & 1 中 1 只有最后一位为 1,其他位皆为 0 ,因此咱们发现 a & 1的做用就是判断 a 的最后一位是否为 1 ,若是 a 的最后一位为 1 ,a & 1 = 1,不然为 0 。因此咱们还能够经过这个公式来判断 a 的奇偶性。
| 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。

这个公式的做用就是将咱们移位后的 res 的最后一位 0 变为 1。这个 1 也就表明着咱们只出现一次元素的某一位。
只出现一次的数Ⅲ
给定一个整数数组 nums,其中刚好有两个元素只出现一次,其他全部元素均出现两次。找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5] 输出: [3,5]
这个也很容易理解,算是对第一题的升级,第一题有 1 个出现 1次的数,其他出现两次,这个题目中有 2 个出现 1次的数,其他数字出现两次。那么这个题目咱们怎么作呢?咱们看一下能不能利用第一题中的作法解决。
HashSet
这个作法和咱们第一题的作法一致,只要理解了第一题的作法,这个很容易就能写出来,有一点不一样的是,第一题的 HashSet 里面最后保留了一个元素,该题保留两个元素。
题目代码
位运算
第一题中,咱们能够经过异或运算直接求出目标数,可是咱们第二题中不能直接用异或,是由于其余数字都出现三次,目标数出现一次。在这个题目中其余数字出现两次,目标数出现一次,可是此次的目标数为两个,咱们直接异或运算的话,获得的数则为两个目标数的异或值,那么咱们应该怎么作呢?
咱们试想一下,若是咱们先将元素分红两组,而后每组包含一个目标值,那么异或以后,每组获得一个目标值,那么咱们不就将两个目标值求出了吗?
例:a,b,a,b,c,d,e,f,e,f 分组后
A组:a, a , b, b, c 异或获得 c
B组:e, e, f, f, d 异或获得 d
原理懂了,那么咱们应该依据什么规则对其进行分类呢?
c , d 两个不一样的数,那么二进制上一定有一位是不一样的,那么咱们就能够根据这一位(分组位)来将 c , d 分到两个组中,数组中的其余元素,要么在 A 组中,要么在 B 组中。
咱们应该怎么获得分组位呢?
咱们让 c , d 异或便可,异或运算就是对应位不一样时得 1 ,异或以后值为 1 的其中一位则为咱们分组。
例 001 ⊕ 100 = 101,咱们能够用最右边的 1 或最左边的 1 作为分组位,数组元素中,若咱们将最右边的 1 做为咱们的分组位,最后一位为 0 的则进入 A 组,为 1 的进入 B 组。
那么咱们应该怎么借助分组位进行分组呢?
咱们处理 c , d 的异或值,能够仅保留异或值的分组位,其他位变为 0 ,例如 101 变成 001或 100
为何要这么作呢?在第二题提到,咱们能够根据 a & 1 来判断 a 的最后一位为 0 仍是为 1,因此咱们将 101 变成 001 以后,而后数组内的元素 x & 001 便可对 x 进行分组 。一样也能够 x & 100 进行分组.
那么咱们如何才能仅保留分组位,其他位变为 0 呢?例 101 变为 001
咱们能够利用 x & (-x) 来保留最右边的 1

题目代码:
本文分享自微信公众号 - 五分钟学算法(CXYxiaowu)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。