找出数组中只出现一次的数字

简介: 找出数组中只出现一次的数字

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 与(&)运算符:



  1. a & b只有a、b同时为1,则a & b = 1。1 & 1 = 1、1 & 0 = 0、0 & 1 = 0、0 & 0 = 0。
  2. 作用:查询,找相同。寻找两个数字共同为1的bit位。


2.1.2 或(|)运算符:


  1. a | b a、b任何一个为1,则a | b = 1。1 | 1 = 1、1 | 0 = 1、0 | 1 = 1、0 | 0 = 0。
  2. 作用:赋值。将a上与b为1的bit位对应的bit位置为1。


2.1.3 非(~)运算符


  1. 取反操作,~1 = 0,~0 = 1。
  2. 作用:结合&操作 清除。a &= ~b。


2.1.4 异或(^)


  1. a ^ b 只有ab互斥,则a ^ b = 1。1 ^ 0 = 1、0 ^ 1 = 1、0 ^ 0 = 0、1 ^ 1 = 0。
  2. 作用:查询,找不同。
  3. 速记:阴阳互补,只有男人和女人才能生出小孩。连连看游戏中相同图案可以抵消掉。


2.2 解题思路



记录每个bit为1的次数。出现1次存在ones上。出现2次存在twos上。出现3次清除掉。

  1. 由于要求时间复杂度为O(n),所以循环遍历数组。


  1. 设置两个变量 ones,twos。ones含义是,元素bit位上值为1出现1次,赋值给ones。ones = ones^arr[i]。 twos的含义是:如果当前遍历的元素bit位上值为1出现2次时,赋值给twos。


  1. 如果当前遍历的元素bit位上值为1出现了3次,则将ones和twos该bit位上的值清零。


  1. 最终ones的值就是只出现一次的数字。


几个疑惑?


  1. 为什么ones要用异或(^)操作?


答:以 { 3, 3, 2, 3 }为例。第一次遍历,ones = 3。*第二次遍历,arr[1] =3。ones需要变成0。需要抵消掉arr[1]。有点类似消消乐的意思。所以用异或操作。


  1. 如果判断一个元素哪些位上值为1出现了三次?


答:ones & twos。

演算

以{ 3, 3, 2, 3 }为例


  1. 第一次循环 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


  1. 第二次循环 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


  1. 第三次循环 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


  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

相关文章
|
5月前
数一下 1到 100 的所有整数中出现多少个数字9并输出这些数字
数一下 1到 100 的所有整数中出现多少个数字9并输出这些数字
66 0
|
5月前
|
索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
删除数组中指定的数字
删除数组中指定的数字
82 0
7-10 求数字个数
7-10 求数字个数
81 0
|
存储 算法 JavaScript
寻找数组中的重复数字
寻找数组中的重复数字
寻找数组中的重复数字
|
JavaScript 前端开发
数字和字符串相加
数字和字符串相加
122 0
|
算法
如何在不把数字转为字符串的前提下反转数字
算法问题:如何在不把数字转为字符串的前提下反转数字
60 0
|
存储 算法 Python
python 力扣算法实现2 :#给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 # #最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
python 力扣算法实现2 :#给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 # #最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
找出数组中只出现一次的数字
找出数组中只出现一次的数字
114 0