数组中最出现一次的元素
来源:互联网 发布:及时生成字幕软件 编辑:程序博客网 时间:2024/06/07 05:53
问题:给定一个数组 a0,a1,a2,...an ,其中只有两个元素出现一次,其它的元素都出现两次,找出这两个元素是多少。要求时间复杂度为 n,空间复杂度为 1。
这个问题可以快速排序(就地排序,不用额外空间)再遍历。但这样的时间复杂度为 n*lgn,不满足要求。或者可以使用某种哈希结构用来记录每个元素出现的情况,最后遍历。但这样做需要额外的空间,也不满足要求。
那么,我们只能另外想办法了。问题中的条件我们没有充分利用,“其它的元素都出现两次”,为什么是两次,不是三次?这里有何玄机?我们能不能将出现两次的元素这些噪声数据去掉,只剩下我们要求的数据呢?答案是利用异或运算,两个相同的数异或后等于 0,任何数与 0 异或等于本身。
于是,我们可以把 a0,a1,...an 全部异或起来,结果 g = x ^ y,这里的 x 和 y 就是所求的两个只出现一次的元素。现在的问题是,我们手上只有一个数 g ,那么,如何把它利用起来呢?
我们设想一种简单的问题,如果这个数组中只有一个出现一次的数,那么,g 就是所求的数。
我们随时要有分治以及合并的思想!
观察一下,将这个简单的问题合并不就是所求的问题吗?现在有两个数组,a0,a1,a2...an 和 b0,b1,b2...bn ,两个数组里面各只有一个出现一次的数字,其它的数都出现了两次,且这两个数组没有公共的数字。我们合并这两个数组,不就是所求的问题了吗?
反过来,我们能不能把所求的问题转换为两个简单的问题再解决呢?设两个出现一次的元素分别为 x 和 y。
转换有两个关键点:
1.将 x 和 y 划分到不同的数组中。
2.相同的元素要划分到同一个数组中。
这实在是一个比较难的问题,因为现在可以利用的条件只有 g 这个值。划分需要借用一个技巧,这里直接给出吧。
我们想像 g 上面为 1 的那些位(x != y,所以 g 不 0,因此 g 不可能全部位都为 0,必存在某一位为 1),1 = 0 ^ 1 = 1 ^ 0。我们可以利用某一个位上的值(0 或者 1)来划分数组,刚好它把 x 和 y 区分开。我们任意选择 g 的二进制位上为 1 的那一位,设为第 p 位。作如下划分:
(原问题的)数组 a0,a1,a2...an 中,每个元素的值的第 p 位为 1 的划分到 A1 数组,否则划分到 A2 数组。再分别求这两个数组的惟一元素。当然,并不能真正构建两个数组,因为问题有空间复杂度的限制。下面给出相关核心代码:
问题推广
如果问题改为:给定一个数组 a0,a1,a2,...an ,其中只有三个元素出现一次,其它的元素都出现两次,找出这三个元素是多少。
我们还用原来的办法来做,划分为两个数组的时候会遇到麻烦。因为求所有元素异或的结果 g 可能等于0。(三个不相同的数字异或可能为0,如a = 1,b = 2,c = 3时,a ^ b ^ c = 0)。这是一个不好解决的点。
退一步讲,设 g != 0,取 g 的二进制中最后一次出现 1 的位为 b ,因为 1 = 0 ^ 1 ^ 0 = 1 ^ 1 ^ 1;也就是说,存在两种情况:设三个只出现一次的元素是 x , y ,z,
1. x,y,z第 b 位全为 1
2.某两个第 b 位为0,一个为1
如果是第二种情况还比较好解决,我们先处理那个第 b 位为 1 的元素所在的数组,可以得到这个元素,那么,我们在原数组中去掉这个元素,于是问题又转换为了上面一个问题。
如果是第一种情况,似乎是不好解决的。
综上,这个问题与上面的问题不太一样,不能以 g 的二进制位为 1 来划分数组。得另外想办法。但我们必须牢牢记住一点,我们可以用于划分的条件只有 g。所以,我们还是得围绕 g 来想办法。我们用 g 与每一个元素再异或,能不能有所发现呢?设三个所求的元素是 x,y,z。
我们现在的任务是找到可区分 g ^ x 和 g ^y 和g ^ z 这三个数字的条件。如果要划分,肯定还是以某位是否为 1 来划分的,这意味着,先要证明这三个数都是非 0的。
由于g = x ^ y ^ z ^ (其它元素两两相异或) = x ^ y ^ z,那么:
g ^ x = y ^ z
g ^ y = x ^ z
g ^ z = x ^ y
而 x,y,z三个数字两两不等,所以 g ^ x , g ^ y , g ^ z 都不为 0;以这三个数字分别为 gx,gy,gz,延续之前的思路,能不能以 gx,gy,gz 某一位为 1 的位 b 作为区分条件呢?这就意味着,这三个数字分别的 b,即b1, b2 ,b3 不能全部为 1,否则不能区分。
我们用反证法来证明。如果这三个数字的第 b 位都为1,则 y ^ z ,x ^ z ,x ^ y 的第 b 位都为1。可以假设 y, z 第 b 位分别为 0,1或者1,0,而 x ,z 第 b 位分别为 0,1或者 1 ,0;z 为假设的共同所在,所以存在以下取值:
y,z,x : 0,1,0 或 1,0,1。这两种取值都导致 y ^ x = 0,这与之前的假设矛盾,所以 y ^ z ,x ^ z ,x ^ y 的第 b 位不都为 1。
其实也可以这样证明,由于 gx ^ gy ^ gz = (y ^ z) ^ (x ^ z) ^ (x ^ y) = g ^ g ^ g ^ x ^ y ^ z = g ^ g ^ g ^ g = 0,所以,不可能出现 gx , gy ,gz 这三个数字的某一位同时为 1,否则 gx ^ gy ^ gz 就不为 0 了,与上面矛盾,而且,这三个数字的同一位上的值只有可能是这样的:两个 1,一个0;或者三个 0;
因此,可以用这样的步骤去划分:
1.计算 gx, gy, gz
2.取 gx 的最后一位为 1 的那个位,记为第 b 位,则 gy , gz 中必有一个的 b 位为1,一个为0;
3.将 b 位上为 1 的划分到一个数组,为 0 的划分到一个数组。
4.找出 b 位为 0 的那个只出现一次的元素,将它从原数组中剔除(放到原数组的末尾)。
5.直接使用上一个问题的解法,找出剩下的两个只出现一次的元素。
至此,问题得解。
- 数组中最出现一次的元素
- 数组中只出现一次的元素
- 找出一个数组中只出现过一次的元素
- 找数组中只出现一次的元素
- 查找数组中只出现一次的元素
- 找数组中只出现一次的元素--位运算
- 找出数组中只出现一次的元素
- Leetcode260. 找出数组中只出现一次的两个元素
- vector数组中删除相同元素(输入的元素输出时只能出现一次)
- 数组中除一个元素外其他所有元素出现二或三次,找到只出现一次的元素
- Single Number--找出数组中唯一的一个只出现一次的元素
- leetcode-singleNum2 找出一个数组中只出现过一次的元素
- 用线性时间复杂度实现找出数组中出现一次的元素
- leetcode_260. Single Number III 数组中找出两个只出现一次的元素
- Leetcode540. 二分查找找出数组中只出现一次的元素
- 一个数组除了一个元素只出现一次,其他元素全都出现了三次,输出出现一次的元素
- 数组中只出现一次的数字
- 数组中只出现一次的数字
- OCP 1Z0 053 147
- 字符串比较问题
- 插入排序
- JDOM操作XML实例
- MySQL-->基础知识-->mysql my.cnf 配置建议
- 数组中最出现一次的元素
- 斗地主系列之牌型大小比较
- Intentservice自己的一点感受关于java.lang.NullPointerException
- poj 1155 TELE
- Keil C51 详细设置
- ubuntu 设置默认文件管理器
- 简单的PHP实现倒计时方法
- shell 面试题
- Qt基类之QWidget