【算法】异或算法巧用——数字出现次数

性质

  • a^b=b^a
  • a^b^c=a^(b^c)
  • a^0=a
  • a^a=0;

在刷题上主要应用的是【异或】的自反省,即A^B^B=A,依此性质出现的题目层>出不穷
应用1:交换a与b

t=a^b;
a=a^t;
b=b^t;

应用2:在一列数组中,其中数字均成双出现,仅有一个数字仅出现一次,求该【单身狗】。
直接将整个数组异或求解,解便是该 【单身狗】

☆☆剑指 Offer 56 - I. 数组中数字出现的次数

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

异或及按位分组——分治

思路

显然,对于nums中仅有1个孤独数字,可直接通过全局异或得到该值。但如何求解2个不同孤独数字呢?应用上述思想,将数组按如下条件分组,便可求得:

  1. 相同数字分为同一组
  2. 孤独数字分为不同组

故该问题转化为如上两个子问题。

设对nums异或所得为x,两个【孤独数字】分别为a,b;将x按位展开 x 1 x 2 . . . x k x_1x_2...x_k x1x2...xk,显然有 a i a_i ai^ b i = x i b_i=x_i bi=xi;取任意 x i x_i xi==1, a i 与 b i a_i与b_i aibi不同。故依据该位进行分组,该位为0的为一组,该位为1的一组,必然满足如上两个条件:

  1. 相同数字该位必然相同,故必然为同一组,成立
  2. 由于 x i x_i xi==1,表示在该位a,b并不相同,所以必然不为同一组,成立

算法

  • 求x
  • 找到x从左到右第一个为1的位
  • 按位分组,并异或求值

实现

vector<int> singleNumbers(vector<int>& nums) {
    int x = 0;
    for (int v : nums)
        x ^= v;
    int div = 1;
    while ((div & ret) == 0)
        div <<= 1;
    int a = 0, b = 0;
    for (int n : nums)
        if (div & n)
            a ^= n;
   	    else
            b ^= n;
    return {a, b};
}

复杂度分析

  • 时间复杂度: O ( N ) O(N) O(N)
  • 空间复杂度: O ( 1 ) O(1) O(1)

☆☆☆137. 只出现一次的数字 II

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

思路

XOR
该运算符用于检测出现奇数次的位:1、3、5 等。
以此类推,只有某个位置的数字出现奇数次时,该位的掩码才不为 0。

因此,可以检测出出现一次的位和出现三次的位,但是要注意区分这两种情况。

算法

AND 和 NOT
为了区分出现一次的数字和出现三次的数字,使用两个位掩码:seen_onceseen_twice
思路是:

  • 仅当 seen_twice 未变时,改变 seen_once
  • 仅当 seen_once 未变时,改变seen_twice

位掩码 seen_once 仅保留出现一次的数字,不保留出现三次的数字。

实现

seen_once=seen_twice=0;
while(){
	seen_once=~seen_twice & (seen_once ^ x);    /* seen_twice未变时,改变seen_once */ 
    seen_twice=~seen_once & (seen_twice ^ x);
}

复杂度分析

  • 时间复杂度: O ( N ) O(N) O(N)
  • 空间复杂度: O ( N ) O(N) O(N)

题目标题难度划分

星级题目难度
简单
☆☆中等
☆☆☆困难

算法掌握难度程度划分

星级掌握难度
普通
经典,易掌握
❤❤经典,略难掌握
❤❤❤困难

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值