1.题目(困难)
给定一个数组,除了一个元素只出现1次,其它元素都出现3次。设计一个算法找出只出现一次的元素。要求时间复杂度为O(n),额外空间O(1)。
1.1 例子
输入: arr[] = {12, 1, 12, 3, 12, 1, 1, 2, 3,3}
输出: 2
除了2其它都出现3次。
输入: arr[] = {10, 20, 10, 30, 10, 30, 30}
输出: 20
除了20其它都出现3次。
1.2 java代码实现
class GFG { static int getSingle(int arr[], int n) { int ones = 0, twos = 0; int common_bit_mask; for (int i = 0; i < n; i++) { twos = twos | (ones & arr[i]); ones = ones ^ arr[i]; common_bit_mask = ~(ones & twos); ones &= common_bit_mask; twos &= common_bit_mask; } return ones; } public static void main(String args[]) { int arr[] = { 3, 3, 2, 3 }; int n = arr.length; System.out.println("只出现一次的元素是 " + getSingle(arr, n)); } }
输出:
只出现一次的元素是 2
2.算法讲解
2.1 按位操作
2.1.1 与(&)运算符:
- a & b只有a、b同时为1,则a & b = 1。1 & 1 = 1、1 & 0 = 0、0 & 1 = 0、0 & 0 = 0。
- 作用:查询,找相同。寻找两个数字共同为1的bit位。
2.1.2 或(|)运算符:
- a | b a、b任何一个为1,则a | b = 1。1 | 1 = 1、1 | 0 = 1、0 | 1 = 1、0 | 0 = 0。
- 作用:赋值。将a上与b为1的bit位对应的bit位置为1。
2.1.3 非(~)运算符
- 取反操作,~1 = 0,~0 = 1。
- 作用:结合&操作 清除。a &= ~b。
2.1.4 异或(^)
- a ^ b 只有ab互斥,则a ^ b = 1。1 ^ 0 = 1、0 ^ 1 = 1、0 ^ 0 = 0、1 ^ 1 = 0。
- 作用:查询,找不同。
- 速记:阴阳互补,只有男人和女人才能生出小孩。连连看游戏中相同图案可以抵消掉。
2.2 解题思路
记录每个bit为1的次数。出现1次存在ones上。出现2次存在twos上。出现3次清除掉。
- 由于要求时间复杂度为O(n),所以循环遍历数组。
- 设置两个变量 ones,twos。ones含义是,元素bit位上值为1出现1次,赋值给ones。ones = ones^arr[i]。 twos的含义是:如果当前遍历的元素bit位上值为1出现2次时,赋值给twos。
- 如果当前遍历的元素bit位上值为1出现了3次,则将ones和twos该bit位上的值清零。
- 最终ones的值就是只出现一次的数字。
几个疑惑?
- 为什么ones要用异或(^)操作?
答:以 { 3, 3, 2, 3 }为例。第一次遍历,ones = 3。*第二次遍历,arr[1] =3。ones需要变成0。需要抵消掉arr[1]。有点类似消消乐的意思。所以用异或操作。
- 如果判断一个元素哪些位上值为1出现了三次?
答:ones & twos。
演算
以{ 3, 3, 2, 3 }为例
- 第一次循环 arr[0] = 3 。
twos = tows|(ones&arr[0])
twos = 00000000|(00000000&00000011) =00000000
ones = ones^arr[0]
ones = 00000000^00000011 = 00000011
common_bit_mask = ~(ones & twos)
common_bit_mask = ~(00000011 & 00000000) =11111111
ones & = common_bit_mask ones = 00000011
twos & = common_bit_mask twos = 00000000
ones = 3,twos = 0
- 第二次循环 arr[1] = 3 。
twos = tows|(ones&arr[1])
twos = 00000000|(00000011&00000011) =00000011
ones = ones^arr[1]
ones = 00000011^00000011 = 00000000
common_bit_mask = ~(ones & twos)
common_bit_mask = ~(00000000 & 00000011) =11111111
ones & = common_bit_mask ones = 00000000
twos & = common_bit_mask twos = 00000011
ones = 0,twos = 3
- 第三次循环 arr[2] = 2 。
twos = tows|(ones&arr[2])
twos = 00000011|(00000000&00000010) =00000011
ones = ones^arr[2]
ones = 00000000^00000010 = 00000010
common_bit_mask = ~(ones & twos)
common_bit_mask = ~(00000010 & 00000011) =11111101
ones & = common_bit_mask ones = 00000010 & 11111101 = 00000000
twos & = common_bit_mask twos = 00000011 & 11111101 = 00000001
ones = 0,twos = 1
- 第四次循环 arr[3] = 3 。
twos = tows|(ones&arr[3])
twos = 00000001|(00000000&00000011) =00000001
ones = ones^arr[3]
ones = 00000000^00000011 = 00000011
common_bit_mask = ~(ones & twos)
common_bit_mask = ~(00000011 & 00000001) =11111110
ones & = common_bit_mask ones = 00000011 & 11111110 = 00000010
twos & = common_bit_mask twos = 00000001 & 11111110 = 00000000
ones = 2,twos = 0