《剑指offer》位运算相关题目详解

文章介绍了位操作符如移位和按位与、或、异或在解决编程问题中的应用,包括计算二进制中1的个数、无加减乘除做加法以及找数组中出现次数不同的数字等。通过位运算的性质,如按位与用于计算汉明重量,按位异或进行无加法的加法操作,以及利用异或寻找数组中出现一次的数字。
摘要由CSDN通过智能技术生成

目录

一、相关操作符

1. 移位操作符

① >>(右移)

② <<(左移)

2. 位操作符

① &(按位与)

② | (按位或)

③ ^(按位异或)

二、题目详解

1) 二进制中1的个数

2)不用加减乘除做加法

3)数组中数字出现的次数

4)数组中数字出现的次数 II


一、相关操作符

1. 移位操作符

① >>(右移)

右移分为两种:算数右移和逻辑右移

算数右移:左边补原来的符号位,右边丢弃(vs通常是算数右移)

逻辑右移:左边补0,右边丢弃

(无符号类型左边补零)

② <<(左移)

左移规则:左边丢弃, 右边补零

注意:不能移动负数位,这是标准未定义的!

2. 位操作符

① &(按位与)

对于两个数  &,有0则为0,两个1才为1

运用:用&计算一个数2进制中1的个数

//计算n的二进制中1的个数
int count = 0;
	while (n) {
		n &= (n - 1);
		count++;
	}

具体原理大家可以自己画图分析😁

② | (按位或)

对于两个数  |,有1则为1,两个0才为0

③ ^(按位异或)

对于两个数 ^  ,相同为0, 相异为1

二、题目详解

1) 二进制中1的个数

 leetcode传送➡️https://leetcode.cn/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/?favorite=xb9nqhhg

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。

示例 1:

输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

题目分析

这是一道位运算入门题目,首先我们知道整数在内存中以补码(二进制)的形式存储,直接遍历二进制中每一位判断是0是1即可;

如何取到二进制中的每一位呢?  我们想到用1依次&(按位与)这个整数的每一位,1的二进制补码除了第一位是1其余全是零,而&“有零则零”,如果1与整数对应的位置是0则两者按位与的结果肯定是0,如果对应的位置是1那么结果则是非0;

最后则需要考虑如何将1的二进制补码中1的位置移动,直接用移位操作符“>>”或“<<”即可

图解

整体代码

int hammingWeight(uint32_t n) {
    int sum = 0;
    for(int i = 31;i>=0;i--)
    {
        if(n&(1u<<i))   //1u 与 (unsigned int) 1 表示含义相同
        sum++;
    }
    return sum;
}

2)不用加减乘除做加法

 leetcode传送➡️https://leetcode.cn/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/?favorite=xb9nqhhg

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

示例:

输入: a = 1, b = 1
输出: 2

题目分析

不能用加减乘除,那么自然就会想到用位运算了,那么用什么操作符呢?

我们知道二进制中两个1相加会向前进一位,0和1相加不需要进位该位得1,那我们就需要将进位与不进位的分开算

&是当两个为1的时候才为1,正符合我们需要的两个1相加,再将按位与的结果向左移一位即可,那么向左移一位后还有可能再次进位啊?所以我们需要一个循环,直到没有进位为止;

接下来就是处理没有进位的:用^按位异或即可——相同为0相异为1

接下来我们用画图的方式进一步理解

图解

整体代码

int add(int a, int b){
    int c = 0;
    while(b != 0){
        c = (unsigned int)(a & b)<<1;   //举例:00111...1111 与 00000...0001
        a ^= b;       
        b = c;
    }
    return a;
}

3)数组中数字出现的次数

leetcode传送➡️https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/?favorite=xb9nqhhg

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

示例 1:

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

题目分析

这道题看起来是非常简单的,当你做下去时会发现相同的很容易用^抵消,但是那两个不相同的怎么也分离不开😤

所以这道题的重点是将两个不相同的数组分离

很容易想到将数组的所有数遍历异或得到一个只有两个不相同的两个数异或的结果(假设这个数为n);

由“相同为0, 相异为1”得,异或得结果中总有至少一位为1,我们找到这一位再以此为划分界限将数组中的所有数异或0,这两个分别划分异或的结果就是这道题的答案了

(有人会问,将不同的两个数分开了,那么数组中的其他数不会有影响嘛?答案是不会的,因为相同的两个数与n2&,得到的结果是一样的,会分到同一边去)

核心代码

int* singleNumbers(int* nums, int numsSize, int* returnSize){
    *returnSize = 2;
    int*ret = (int*)malloc(sizeof(int)*2);
    ret[0] = ret[1] = 0;
    int n = 0;
    //将所有数异或
    for(int i = 0; i < numsSize; i++){
        n ^= nums[i];
    }
    //找到两个数不同的一位(异或为1)
    int n2 = 1;
    while( (n2 & n) == 0){   //&“有0则0”,遇到两个1则说明找到n有1的一位
        n2 = n2 << 1;
    }
    //以n2为界限将数组中的所有数分别与0异或得到两个答案
    for(int i = 0; i < numsSize; i++){
        if((nums[i] & n2) == 0){
            ret[0] ^= nums[i];
        }
        else{
            ret[1] ^= nums[i];
        }
    }
    return ret;
}

4)数组中数字出现的次数 II

leetcode传送➡️https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/?favorite=xb9nqhhg

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

示例 1:

输入:nums = [3,4,3,3]
输出:4

题目分析

分析题目,与之前的两个数不同,这里只有一个不同的数,其余数是三个三个的共同出现;

我们知道二进制位中只有1和0,将一个数分成三十二个位来看,将所有数加起来那么一个位的总和只有3n+1,3n,1三种情况将这一位%3,得到的就只有0和1了,因为其余数都是三个三个的出现,那么%3之后得到的1肯定是那个单独出现的数在该位是1,相反如果为0,那么在该位一定为0;

图解

 

整体代码

int singleNumber(int* nums, int numsSize){
    int result = 0;
    int bit = 0;
    //将三十二位分别计算
    for(int i = 0; i < 32; i++) {
        bit = 0;
        //将数组中所有数的第i位相加
        for(int j = 0; j < numsSize; j++) {
            bit += (nums[j] >> i) & 1;
        }
        //将总和%3加在结果的第i位,表示那位不同的数在该位的值
        result += (bit%3)<<i;
    }
    return result;
}

拓展

以这样的解法思维,可以解在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了N次的题目,只需将总和%N即可

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dusong_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值