数组中只出现一次的两个数-位运算

前言

        由于要准备找工作了,所以要开始刷题了,但是平时疏于做题,一些知识已经悄然忘记,特开此专栏记录我复习的过程

今天做牛客上一道位运算的题,刚开始是暴力求解,然后就想到了位运算。

数组中只出现一次的两个数字_牛客题霸_牛客网

知识讲解

首先,位运算是指将数字转换为二进制的情况下进行的操作

5= 0 1 0 1
6= 0 1 1 0 

位运算分为逻辑位运算符和位移位运算符

逻辑位运算符

位与(&)

当两者都为1时,结果为1

  16 & 5
     |
     V
 0 1 1 1 0
 0 0 1 0 1
 ---------
 0 0 1 0 0

位或(|)

当且仅当两位都为0时,结果为0

  13 | 5
     |
     v
  0 1 1 1
  0 1 0 1
  -------
  0 1 1 1

异或(^)

当且仅当两位不同时,结果为1

  13 ^ 5
     |
     v
  0 1 1 1
  0 1 0 1
  -------
  0 0 1 0

所以异或就有一个性质,就是任何数异或自己都等于0

所以就有  a^b^a=b

用异或还可以进行数据交换

a=a^b

b=a^b

a=a^b

这里就完成了数据的交换

按位取反( ~ )

将所有的数字0变成1,1变成0

   13
0 1 1 1
   |
   V
  ~13
1 0 0 0

位移运算符

左移(<<)

x<<y就表示将x转换为二进制,将所有的位都向左移动y位,然后末位补0,每移动一位就相当于这个数乘了2倍

   5<<2
0 0 0 1 0 1
    |
    v
0 1 0 1 0 0
    20

右移(>>)

x>>y则表示将x转换为二进制,将所有的位都向右移动y位,每移动一位就相当于给x除了2,接着向下取整

        然后,还分为了两种情况,如果x为正则高位补0,x为负则高位补1(这里涉及到了二进制的正负,如果是有符号整数,最高位是符号位,为0则是正数,为1则是负数)

   5>>2
0 0 0 1 0 1
    |
    v
0 0 0 0 0 1
    1

题目思路

        这里题目是找出一个数组中只出现一次的两个数,刚好就能用到异或的性质,由于一个数异或自己等于0,而0异或任何数都等于那个数

        所以首先就定义一个变量a,赋值为0,然后遍历数组,将这个变量与数组中的每个值都异或一遍,由于大部分数都会出现两次,异或后都是0,最后a的值就是那两个只出现一次的值异或的结果

        然后,因为两个数不相等,所以总会有某一位,一个数是1,而另一个数是0

5 =  0 1 0 1
4 =  0 1 0 0

这里的5和4就是,最低位首先就不相同

        所以,第二步就是定义一个变量b,赋值为1,然后与a进行异或,再通过左移运算符一步步找到两个出现次数为1的数的第一个不相同的位

        再然后遍历原数组,通过与b进行与运算,将原数组分成了两部分,然后定义两个变量,分别与这两部分的值进行异或,就能求出出现次数为1的那两个数

        题目还要求按升序返回,简单用if语句判断一下,然后用swap交换一下就行

#include <fstream>
#include <vector>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @return int整型vector
     */
    vector<int> FindNumsAppearOnce(vector<int>& nums) {
        int a=0;
        //首先用a与数组中的每个数都异或一遍
        for(int num:nums)
        {
            a^=num;
        }
        int b=1;
        //接着找出两个出现一次的数第一个不同的位
        while((b&a)==0)
        {
            b<<=1;
        }
        //接着定义两个变量分别求出那两个数
        int num1=0,num2=0;
        for(int num:nums)
        {
            if(num&b)
            {
                num1^=num;
            }
            else 
            {
                num2^=num;
            }
        }
        if(num1>num2) swap(num1, num2);
        vector<int> v={num1,num2};
        return v;

    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值