基础算法-位运算

一、位运算符

1. 位运算符简介

  • 位运算符主要作用于位,是逐位进行操作。最常用的有:与 &、或 |、异或 ^。
  • 常见的位运算符有(假设变量 A = 60,变量 B =13):
运算符描述实例
&二进制与运算符:全 1 为 1 ,遇 0 则 0(A & B)= 12(0000 1100)
|二进制或运算符:全 0 为 0,遇 1 则 1(A | B)= 61(0011 1101)
^二进制异或运算符:相同为 0 ,不同为 1(A ^ B)= 12(0011 0001)
~二进制补码运算符:0 变为 1 ,1 变为 0(~A)= -61(1100 0011)
<<二进制左移运算符:左操作数的值向左移动右操作数指定的位数(A<<2)= 240(1111 0000)
>>二进制右移运算符:左操作数的值向右移动右操作数指定的位数(A>>2)= 15(0000 1111)

2. 为什么要使用位运算符

  • C 语言位运算符在某些编程中如果灵活应用,则可以大大提高程序的执行效率,使程序执行时速度更高。
  • C 语言主要应用于嵌入式开发、智能电器、通信行业等一些对效率和时间都要求很高的应用领域中,学好位运算符,在程序开发中灵活应用位运算符,往往能在这些应用中起到事半功倍的效果。

3. 与运算符

  • 负数按补码形式参加按位与运算。
  • 清零:如果想将一个单元清零,即使其全部二进制位为 0 ,只要与一个各位都为 0 的数值相与,结果为零。
  • 取一个数的指定位:取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低 4 位为1,其余位为0,即Y=0000 1111,然后将 X 与 Y 进行按位与运算即可得到 X 的指定位。
  • 判断奇偶:根据最末位是 0 还是 1 来决定,为 0 就是偶数,为 1 就是奇数。因此可以用 if((a & 1) == 0)代替 if(a % 2 == 0)来判断a是不是偶数。

4. 或运算符

  • 负数按补码形式参加按位与运算。
  • 将一个数据的某些位设置为 1:取数 X=1010 1110 的低4位设置为1,只需要另找一个数 Y,令 Y 的低 4 位为 1 ,其余位为 0,即 Y=0000 1111,然后将 X 与 Y 进行按位或运算即可。

5. 异或运算符

  • 翻转指定位:取数 X=1010 1110 的低 4 位进行翻转,只需要另找一个数 Y ,令 Y 的低 4 位为 1 ,其余位为 0 ,即 Y=0000 1111,然后将X与Y进行异或运算即可。
  • 与 0 相异或值不变。
  • 交换两个数
void Swap(int &a, int &b)
{
    if (a != b)
    {
        a ^= b;
        b ^= a;
        a ^= b;
    }
}

6. 取反运算符

  • 如对 a 按位取反,则得到的结果为 -(a+1) 。
  • 对正数、负数和零都适用。

7. 左移运算符

  • 左边的二进制位丢弃,右边补 0 。
  • 若左移时舍弃的高位不包含 1,则每左移一位,相当于该数乘以 2。

8. 右移运算符

  • 正数左补 0,负数左补 1,右边丢弃。
  • 操作数每右移一位,相当于该数除以 2。

二、位运算例题——二进制中1的个数

题目描述

给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。

输入格式

输入共两行。
第一行包含整数 n。
第二行包含 n 个整数,表示整个数列。

输出格式

共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。

数据范围

1 ≤ n ≤ 100000
0 ≤ 数列中元素的值 ≤ 1e9

输入样例

5
1 2 3 4 5

输出样例

1 1 2 1 2

具体实现

1. 方法一(模板)

1.1 代码注解
  • for(int i=x;i;i-= i&-i)
  • i 表示 i 为真时继续执行。
  • i-= i&-i 表示 i = i - (i&-i)。
  • i & (-i) 表示的2^k,其中 k 表示 i 中从低位到高位的第一个 1 的位索引。
1.2 实现代码
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int x;
        int res=0;
        cin>>x;
        for(int i=x;i;i-= i&-i) 
        {
            res++ ;
        }
        cout<<res<<" ";
    }
    system("pause");
    return 0;
}

2. 方法二( lowbit )

1.1 实现思路
  • lowbit(x) 是 x 的二进制表达式中最低位的 1 所对应的值。
  • 使用 lowbit 操作,每次 lowbit 操作截取一个数字最后一个 1 后面的所有位,每次减去 lowbit 得到的数字,直到数字减到 0 ,就得到了最终 1 的个数。
  • 根据计算机负数表示的特点,如一个数字原码是 1000 1000 ,他的负数表示形式是补码,就是反码 +1 ,反码是 0111 0111 ,加一则是 0111 1000 ,二者按位与得到了1000 ,就是我们想要的 lowbit 操作。
1.2 实现代码
#include<bits/stdc++.h>
using namespace std;

int lowbit(int x)
{
    return x&(-x);
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int x;
        cin>>x;
        int res=0;
        while(x)
        {
            x-=lowbit(x);
            res++;
        }
        cout<<res<<' ';
    }
    system("pause");
    return 0;
}

3. 方法三(暴力枚举)

1.1 实现思路
  • 对于每个数字a,a &1得到了该数字的最后一位,之后将a右移一位,直到位0,就得到了1的个数。
1.2 实现代码
#include <bits/stdc++.h>
using namespace std;

int n;
int a,k;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a;
        k=0;
        while(a)
        {
            k+=a&1;
            a=a>>1;
        }
        cout<<k<<" ";
    }
    system("pause");
    return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

虚心求知的熊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值