剑指 Offer 56 - I. 数组中数字出现的次数(位运算总结)

题目

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
leetcode
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

解题思路

题目要求找到只出现一次的两个数字,并规定了时间复杂度是O(n),空间复杂度是O(1);这么快,显然只能用数学的方法解决。

一.位运算
  1. & 与 两者都为1,结果才为1;
    • (1).清零 a&0=0
    • (2).判断某一位是否为1 10000111 & 10000000= 10000000 ; 00000111 & 10000000 = 0
    • (3).取一个数字的指定位 a&0000 1111,取a都后四位 (2)(3)一个意思
    • (4). (a&b)<<1 二进制加法的进位
  2. | 或 两者都为0,结果才为0
    • (1).对某些位设置位1 a | 0000 1111,后四位设置位1
  3. ^ 异或 两位相同位1,相异为1
    • (1). x^x = 0, x^0 = x; a^b^b=a^0=a;
    • (2).反转指定位 a^0000 1111,后四位翻转
    • (3).二进制无进位相加, a^b
    • (4). 交换两个数 a^=b;b^=a;a^=b;
  4. ~ 取反 0变1,1变0
    • (1).最低位变0 a~1 111~001=110
二、本题的解法是利用与&的(2)和异或^的(1)
  1. 如果只有一个数只出现一次,那么用异或的(1)就可,所有数字全部异或就可以得到答案
  2. 但本题有两个,所有数字异或出来的结果k是这两个数字的异或;
  3. 利用&(2) 某一位不同可以把这两个数字分开,同时可以让其他只出现两次的数字在一起。
  4. 所以要找到一个数字可以让num1和num2分开,因为k是num1和num2的异或,k中所有为1的位都是二者的不同,找到一个1就可以
    10000111 & 10000000= 10000000 ; 00000111 & 10000000 = 0

代码

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        //先将所有元素异或。最后剩下的数是只出现一次的数字num1,num2的异或
        int k = 0;  // 0^x = x
        for(int i : nums)       //所有数字异或
            k ^= i; 
        int mask = 1;
        while((k&1)!=1)
        {  
            //从后往前找到最低位的1;
            k= k>>1;
            mask = mask<<1;
        }
        int num1=0,num2=0;
        //用这个mask就可以把两个数区分开,
        for(int i : nums)
        {
            if((i&mask)!=0) num1^=i;  // mask指定的那一位是num1,num2的不同
            else num2^=i;
        }
        return {num1,num2};
        
    }
};

补充题:不用加减乘除做加法

**题目:**写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

算法思路:利用&的(4)和^的(3)

class Solution {
    public int add(int a, int b) {
        int sum,carry;
        //和与进位
        do{
            sum = (a^b);
            carry = (a&b) << 1;
            a = sum;
            b = carry;
        }while(b!=0);
        return a;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值