【位运算骚操作】利用位运算实现特定情况数组去重

306 阅读2分钟

前言

  • 上周刷到两道位运算的题目,觉得两道题目挺有代表性的,写篇文章记录一下具体的解题思路

题目

1. 剑指 Offer 56数组中数字出现的次数

题目描述:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 示例 1:

输入:nums = [4,1,4,6] 输出:[1,6] 或 [6,1] 示例 2:

输入:nums = [1,2,10,4,1,4,3,3] 输出:[2,10] 或 [10,2]

  • 解题思路:

    1. 由于除了那两个独特的数「a,b」之外,其他数都是成对出现的,所以可以敏锐的发现当将 nums中所有的数进行异或操作 时,最后得到的结果必定是 a^b
    2. 通过从右往左对比a、b的二进制位,找出a、b第一个不同的二进制位 pointer
    3. 然后通过将nums的所有数据和pointer进行与操作,根据结果是否为0来对nums中的数据进行分组
    4. 分组完成后,a、b明显处于两个不同的组,则两个不同的组自身分别进行异或后得到的结果就是a、b(因为那些出现两次的数必定处在一个组)
  • 代码实现

    func singleNumbers(nums []int)[]int{
      ret := 0
      for _,val := range nums{
        ret ^=val
      }
      pointer := 1
      for pointer&ret==0{// 因为a,b相同的位的异或结果必定是0
        pointer = pointer<<1
      }
      a,b := 0,0
      for _,val := range nums{
        if pointer&val != 0 {
          a ^= val // 进行分组
        }else{
          b ^= val
        }
      }
      return []int{a,b}
    }
    

剑指 Offer 56 - II 数组中数字出现的次数 II

题目描述:在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:

输入:nums = [3,4,3,3] 输出:4 示例 2:

输入:nums = [9,1,7,9,7,9,7] 输出:1

  • 解题思路:

    1. 由于除了独特的数a,其他数都出现三次,则可根据位运算联想到将数组中所有数的每一位二进制位进行单独相加后得到的对应位数的结果的每一位mod 3 的结果必定是a的对应二进制位上的数值。 Picture1.png
    2. 联想每一位数的二进制相加时,结果只能为0,1,2(对应00,01,10),分别设置高位为two,低位为one,可以利用有限状态自动机来表示对应的状态转变过程 Picture3.png
  • 代码实现:

    func singleNumber(nums []int) int{
      one,two := 0,0
      for _,val:=range nums{
        one = one^val&^two// ^two表对two取反,one^val 是为了获取加上对应数后对应为对数值变化,&^two则保证当two==1时则保证,one不能为1,从而确保每一位二进制位的结果在0,1,且不会产生进位,最后nums遍历完毕后最后one即为对应独特的a
        two = two^val&^one
      }
      return one
    }