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

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

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

相关文章
|
10月前
数一下 1到 100 的所有整数中出现多少个数字9并输出这些数字
数一下 1到 100 的所有整数中出现多少个数字9并输出这些数字
113 0
|
4月前
计算字符串中的元音、辅音、数字、空白符
【10月更文挑战第32天】计算字符串中的元音、辅音、数字、空白符。
38 1
|
10月前
28.求任意一个整数的十位上的数字
28.求任意一个整数的十位上的数字
92 3
|
10月前
|
索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
Leetcode 给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引
7-10 求数字个数
7-10 求数字个数
104 0
输出整数各位数字
输出整数各位数字
104 0
|
JavaScript 前端开发
数字和字符串相加
数字和字符串相加
151 0
|
算法 Java C#
数组中数字出现的个数(剑指offer 56-I)
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
|
算法
如何在不把数字转为字符串的前提下反转数字
算法问题:如何在不把数字转为字符串的前提下反转数字
87 0
|
算法 C++
每日算法刷题Day16-和为S的两个数字、数字排列、二进制中1的个数
⭐每日算法题解系列文章旨在精选重点与易错的算法题,总结常见的算法思路与可能出现的错误,与笔者另一系列文章有所区别,并不是以知识点的形式提升算法能力,而是以实战习题的形式理解算法,使用算法。
94 0
每日算法刷题Day16-和为S的两个数字、数字排列、二进制中1的个数