异或运算算法相关

  一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字?”这是经典的算法题,乍看这个题的思路特别多。
  
  这道题的实现方法很多,关键是找到最好的实现方法很难,本文就介绍采用异或运算实现这道题目的解法。

  异或运算是C语言中位运算的一种操作,这种操作对于嵌入式程序员可能比较熟悉,但是对于一般的程序员可能运用的比较少,异或操作具有如下的特征:
  0^num = num; 1^num = ~num; num ^ num = 0; 其中num = 0或者1。
  a^a^b^b^c = (a^a)^(b^b)^c=0^0^c=c;

  需要注意:如果有a + b = c; 则有可能使得a ^ b ^ c = 0;这个条件是非充分非必要,比如a = 1,b = 2, c = 3,这时候的a ^ b ^ c = 0是成立的,但是a = 2, b = 2, c = 4,则是不成立的。
  
  假设数组元素为a[N],其中N的值很大,不成对的元素为an,am。实现上述过程的步骤如下所示:
  首先,变量元素对所有元素进行异或操作,得到的结果肯定是an^am。也就说通过异或操作以后,结果中保存了an和am的特征。由于am和an不同,am^an的结果肯定是大于等于1。am和an不同,那么am^an中为1的某一个bit肯定是am或者an中某一个的特征。
  然后,定义两个值num1,num2,分别用来计算an、am,选择am^an中的某一个bit作为特征位。
  假设是第K位是特征位,再次对元素进行遍历:
  如果元素的第K位是1,这个元素可能是am或者an,那么将当前元素与num1进行异或操作;
  如果元素的第K位为0,那么这个元素则可能是另一个值,那么将当前元素与num2进行异或操作。
  这样遍历完所有元素,因为大部分数据成对出现,根据异或运算的特征,num1,num2就分别保存了两个不同的值,这两个值即为我们所求。
  由上面的分析可知,这种算法只需要遍历两次数组空间即可实现数据的判定,这样时间复杂度为O(N),同时因为没有hashmap之类的结构体,这样空间复杂度就是O(1)。这种算法的实现肯定是最佳的。相比前面提到的hashmap、排序算法时间复杂度和空间复杂度都要小,因此这种算法的实现应该是最佳的。
 

 例如:1 2 3 4 7 3 2 1 4 9
 异或和即7^9=1110,取倒数第二位为特征位;
 对于2 3 7 3 2 它们的倒数第二位都为1,异或和为7;
 对于1 4 1 4 9 它们的倒数第二位都为0,异或和为9;

代码实现:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int N=1000005;
int a[N];
int getBit(int n){//得到特征位
    int i;
    for(i=1;;i++){
        if(n&1){
            return i;
        }
        else n=n>>1;
    }
}
int getBit2(int n,int i){//得到每个元素特征位的元素
    int ans=0;
    for(int j=1;j<=i;j++){
        ans=n&1;
        n=n>>1;
    }
    return ans;
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            scanf("%d",&a[i]);
        }
        int ans=0;
        for(int i=1;i<=n;i++) {
            ans^=a[i];
        }

        int ansbit=getBit(ans);

        int num1=0,num2=0;
        for(int i=1;i<=n;i++){
            if(getBit2(a[i],ansbit)==1){
                num1^=a[i];
            }
            else num2^=a[i];
        }
        printf("%d %d\n",min(num1,num2),max(num1,num2));


    }
    return 0;
}
/*
3

10
1 2 3 4 7 3 2 1 4 9

6
2 2 1 1 3 4
4
1 1 3 4
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值