数组中数字出现的次数

通用思路

通常遇到“出现的次数”,“寻找出现多少次的值”,往往都是可以用哈希表来解决的,因为哈希表的特点天然决定了它的好处:查找,插入,删除的复杂度均为O(1),并且存放形式支持key-value,那么我们只需要将key设置为对应的字符或者其他类型,value设置为出现次数,遍历数组过程中就可以更新对应的value值即可。
这当然是没问题的,并且时间复杂度往往为O(N),但是,由于需要使用哈希表存储,空间复杂度往往也是O(N)的。并且,哈希表在解决各种问题时,并没有利用到特定问题特定分析的想法,比如下面一道题中其他数字均出现两次,并且只有两个数字出现一次,这些特点哈希表都是没有利用到的。这也是哈希表的缺点之一。
面试中,面试官会要求,或者询问我们是否有空间复杂度为O(1)的算法,这个时候就需要换其他的思路来思考了。
接下来,我将由简单到复杂,一步步分析不同的问题的具体方法,最后延申到解决同类型大部分问题的思路。

问题一:其他数字均出现2次,只有一个数字出现一次

这是最简单的一种情况,其他数字均出现两次,只有一个数字出现一次,我们的目标是找出这个只出现一次的数字。如果熟悉位运算,就会想到两个相同的数字,按位异或运算后便是0。
好了,这一句话,这个问题便解决了,我们只需要将所有的数字异或一遍,最终的结果就是只出现一次的那个数字。思路十分简单,充分利用了“两次”和“一次”的特点。
所有的操作就是遍历数组,同时更新保存异或值的变量。时间复杂度O(N),空间复杂度O(1)。是不是比哈希表的方法要优化很多。由于过于简单,具体的代码不在这里写了。

问题二:其他数字均出现2次,只有两个数字出现一次(LC)

这道题和上一个很像,唯一的区别是有两个出现一次的数字,那么还可以像上一题那样直接异或嘛?
显然,是不能的。试想,如果将所有数字异或,出现两次的数字的确变成了0,但是还有两个出现一次的数字呢?最后的结果就是这两个数字的异或。可是不同的两个数字可以产生同样的异或,所以我们没办法根据异或得知两个数字是多少。
既然不能直接异或,那该如何解决?既然不能一次性找出两个数字来,那么能否一个一个找出来呢?或者说我们将这两个数字分组,并且其他所有的数字中相同的一对一定在同一组中,这样分组异或后得到的不就是两个数字吗?可问题是如何分组,才能既保证两个只出现一次的数字不在同一组,并且同样的数字一定在同一组呢?
这里就有一个很巧妙的方法,按位异或的原则是如果两个数字中该位不相同,即一个数字该位为0,而另一个为1,那么异或的结果中该位就为1!
好像有点东西,现在我们可以知道这两个数字异或的结果,并且肯定不等于0(因为两个不同的数字异或不可能为0),那么其二进制中一定存在至少一位为1,我们从为1的位中任意选取一位,用这一位来区分所有的数字,并将这一位为1的数字放在一个数组,这一位为0的数字放在另一个数组。首先这两个只出现一次的数字肯定不相同,并且相同的数字该位肯定也相同,自然会分到一个数组中。
到这里,问题基本解决了,最后只需要分别遍历这两个数组,并求出两个异或值,这就是最终的两个只出现一次的数字。
分析一下,首先需要对整个数组求一次异或,时间复杂度为O(N),之后需要找到这个结果中任意一位不为0的位,这是常数级别的复杂度,然后需要分组异或,这里可以用两个变量来保存两个异或的结果,省去建立数组的空间,遍历过程中根据对应位的值为0还是1,来判断和哪个变量进行异或。数组遍历结束,得到的结果就是两个只出现一次的数字。时间复杂度依然是O(N),空间复杂度为O(1).
因此,总的时间复杂度为O(N),空间复杂度为O(1)。依然要比哈希的效率高很多。
接下来,贴出解决这个问题的代码:

    vector<int> singleNumbers(vector<int>& nums) {
   
        int xor_ans 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值