筑基五层 —— 位运算看这篇就行了
目录
一.修炼必备
1.入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)
2.趁手武器:印象笔记/有道云笔记
3.修炼秘籍:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)
4.雷劫必备:leetcode 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
注:遇到瓶颈怎么办?百度百科_全球领先的中文百科全书 (baidu.com)
二. 位运算
1.什么是位运算?
——位运算就是直接对整数在内存中的二进制位进行操作的运算
2.位运算操作符
位操作符 | 特点 |
&(按位与) |
有0为0,全1为1 |
|(按位或) | 有1为1,全0为0 |
^(按位异或) | 相同为0.不同为1 |
~(按位取反) | 0变1,1变0 |
注:
1)二进制的运算均是以补码进行运算的
2)异或满足交换律和结合律
交换律:num ^ 0 = num; 0 ^ num = num;
结合律:num ^ a ^ a = num; a ^ num ^ a = num;
//任何数和0异或等于本身,相同数异或为0
3)只有对整数才能进行位运算,浮点数不能进行位运算
3.一个小case快速了解位运算符
#include<stdio.h>
int main()
{
int a=7;
int b=4;
//按位与
int c=a&b;
//00000000 00000000 00000000 00000111 -> a: 7
//00000000 00000000 00000000 00000100 -> b: 4
//00000000 00000000 00000000 00000100 -> a&b: 4
printf("%d & %d = %dn",a,b,c);// 7 & 4 = 4
//按位或
c=a|b;
//00000000 00000000 00000000 00000111 -> a: 7
//00000000 00000000 00000000 00000100 -> b: 4
//00000000 00000000 00000000 00000111 -> a|b: 7
printf("%d | %d = %dn",a,b,c);// 7 | 4 = 7
//按位异或
c=a^b;
//00000000 00000000 00000000 00000111 -> a: 7
//00000000 00000000 00000000 00000100 -> b: 4
//00000000 00000000 00000000 00000011 -> a^b: 3
printf("%d ^ %d = %dn",a,b,c);// 7 ^ 4 = 3
//按位取反
c = ~a;
//00000000 00000000 00000000 00000111 -> a: 7
//11111111 11111111 11111111 11111000 -> ~a(补码)
//11111111 11111111 11111111 11110111 -> 反码
//10000000 00000000 00000000 00001000 -> 原码(打印的是原码):-8
printf("~%d = %dn", a, c);//~7 = -8
return 0;
}
4.常用的位操作符
1)判断奇偶性
奇数:(num & 1) == 1 等价于 num % 2 == 1
偶数:(num & 1) == 0 等价于 num % 2 == 0
注:为什么num&1要加括号? —— ==的优先级比&的优先级高
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//若是想要使用num & 1 == 1,则需要使用括号括上(num & 1) == 1
if (num & 1)
{
printf("%d是奇数n", num);
}
else
{
printf("%d是偶数n", num);
}
return 0;
}
2)计算二进制中有几个1:num = num & (num -1)
#include <stdio.h>
int getOneCount(int num)
{
int count = 0;
while (num)
{
num = num & (num - 1);
count++;
//测试用例1:7
//00000000 00000000 00000000 00000111 : 7
//00000000 00000000 00000000 00000110 : 6
//00000000 00000000 00000000 00000110 : 6 :结果
//00000000 00000000 00000000 00000101 : 5
//00000000 00000000 00000000 00000100 : 4: 结果
//00000000 00000000 00000000 00000011 : 3
//00000000 00000000 00000000 00000000 : 0: 结果 -->跳出循环
//测试用例2:10
//00000000 00000000 00000000 00001010 : 10
//00000000 00000000 00000000 00001001 : 9
//00000000 00000000 00000000 00001000 : 8: 结果
//00000000 00000000 00000000 00000111 : 7
//00000000 00000000 00000000 00000111 : 0: 结果 -->跳出循环
}
return count;
}
int main()
{
int num = 0;
scanf("%d", &num);
int count = getOneCount(num);//计算1的个数
printf("%d的二进制中有%d个1n", num, count);
return 0;
}
3)num & -num:得到二进制的最低位的1(不一定是最后一位)
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = num & -num;
printf("%d & -%d = %dn", num, num, ret);
//说明:二进制的运算均是以补码进行的
//测试案例1:7
//10000000 00000000 00000000 00000111 -->原码 -7
//11111111 11111111 11111111 11111000 -->反码
//11111111 11111111 11111111 11111001 -->补码
//00000000 00000000 00000000 00000111 -->补码 7
//00000000 00000000 00000000 00000001 --> 1 :结果
//测试用例2: 10
//10000000 00000000 00000000 00001010 -->原码 -10
//11111111 11111111 11111111 11110101 -->反码
//11111111 11111111 11111111 11110110 -->补码
//00000000 00000000 00000000 00001010 -->补码 10
//00000000 00000000 00000000 00000010 -->2 : 结果
return 0;
}
4)num & ~num:0
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = num & ~num;
printf("%d & ~%d = %dn", num, num, ret);
//测试用例1:3
//00000000 00000000 00000000 00000011 :3
//11111111 11111111 11111111 11111100 :~3
//00000000 00000000 00000000 00000000 :0 结果
//测试用例2:4
//00000000 00000000 00000000 00000100 :4
//11111111 11111111 11111111 11111011 :~4
//00000000 00000000 00000000 00000000 :0 结果
return 0;
}
5)低位首0变1:num | (num+1)
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = num | (num + 1);
printf("%d | %d = %dn", num, num + 1, ret);
//测试用例1:5
//00000000 00000000 00000000 00000101 :5
//00000000 00000000 00000000 00000110 :6
//00000000 00000000 00000000 00000111 : 7 --> 低位首0变1
//测试用例2:9
//00000000 00000000 00000000 00001001 :9
//00000000 00000000 00000000 00001010 :10
//00000000 00000000 00000000 00001011 :11 --> 低位首0变1
return 0;
}
6)求num的相反数:~num + 1
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = ~num + 1;
printf("%dn", ret);
//测试用例1:5
//11111111 11111111 11111111 11111010 :~5
//11111111 11111111 11111111 11111011 :~5 + 1 补码
//11111111 11111111 11111111 11111010 :反码
//10000000 00000000 00000000 00000101 :原码 --> -5
//测试用例2:-5
//10000000 00000000 00000000 00000101 原码
//11111111 11111111 11111111 11111010 反码
//11111111 11111111 11111111 11111011 补码
//00000000 00000000 00000000 00000100 ~(-5)
//00000000 00000000 00000000 00000101 ~(-5)+ 1 --> 5(结果)
return 0;
}
7)判断数是不是2的幂:num & (num-1) == 0
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if ((num & (num - 1)) == 0)
{
printf("%d是2的幂n", num);
}
else
{
printf("%d不是2的幂n", num);
}
//测试案例1:2
//00000000 00000000 00000000 00000010 :2
//00000000 00000000 00000000 00000001 :1
//00000000 00000000 00000000 00000000 :0 --> 结果
//测试用例2:8
//00000000 00000000 00000000 00001000 : 8
//00000000 00000000 00000000 00000111 : 7
//00000000 00000000 00000000 00000000 : 0 -->结果
return 0;
}
8)异或应用:变量交换
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
//交换变量
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("%d %dn", a, b);
return 0;
}
二.移位运算符
1.什么是移位运算符?
——在C语言中,使用<<和>>对数的二进制位进行向左移动和向右移动,而<<和>>称为移位运算符
2.移位操作符
移位操作符 | 特点 |
<< | 左边丢弃,右边补0 |
>> |
算术右移:右边丢弃,左边补符号位 逻辑右移:右边丢弃,左边补0 |
二进制位进行移位的时候,不要移动负数位,因为这个标准是未定义的
3.一个小case快速了解移位操作符
#include <stdio.h>
int main()
{
int num = 7;
int ret = num << 2;
//00000000 00000000 00000000 00000111 num
//000000 00000000 00000000 0000011100 num << 2 :28
printf("%dn", ret);//28
ret = num >> 2;
//00000000 00000000 00000000 00000111 num
//注:我们在这个地方进行的是算术右移,现在大多数编译器都是进行算术右移的
//0000000000 00000000 00000000 000001 num >> 2 :1
printf("%dn", ret);//1
return 0;
}
4.常用的移位操作
1)num >> 1 等价于 num / 2
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = num >> 1;
int res = num / 2;
//测试案例1:10
//00000000 00000000 00000000 00001010 num: 10
//00000000 00000000 00000000 00000101 num >> 1:5
//num / 2 = 5
//测试案例2:15
//00000000 00000000 00000000 00001111 num:15
//00000000 00000000 00000000 00000111 num >> 1:7
//num / 2= 7
if (ret == res)
{
printf("num>>1和num/2相等n");
}
else
{
printf("num>>1和num/2不相等n");
}
return 0;
}
2)去掉num的低x位(从最低位一直到x位):num >> x
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = num >> 3;//去掉低三位
//00000000 00000000 00000000 00001111 num:15
//00000000 00000000 00000000 00000001 num:1
printf("%dn", ret);//1
ret = num >> 2;//去掉低两位
//00000000 00000000 00000000 00001111 num:15
//00000000 00000000 00000000 00000011 num:3
printf("%dn", ret);//3
return 0;
}
三.位运算综合使用
注:规定,我的最低位是从1开始,最高位是32
1.获取num二进制位的第n位值:(num >> n) &1
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int ret = (num >> 3) & 1;
printf("%dn", ret);
//测试案例:15
//00000000 00000000 00000000 00001111 num:15
//00000000 00000000 00000000 00000001 num >> 3
//00000000 00000000 00000000 00000001 (num >> 3) & 1
//00000000 00000000 00000000 00000001 1
return 0;
}
2.将num的第(n+1)位置为1:num | (1 << n)
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//将num的第4位置为1
int ret = num | (1 << 3);
printf("%dn", ret);
//测试案例:5
//00000000 00000000 00000000 00000101
//00000000 00000000 00000000 00001000 1 << 3
//00000000 00000000 00000000 00001101 13
return 0;
}
3.将num的第(n+1)位置为0:num & (~(1 << n))
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//将num的第3位置为0
int ret = num & (~(1 << 2));
printf("%dn", ret);
//测试案例:5
//00000000 00000000 00000000 00000100 1 << 2
//11111111 11111111 11111111 11111011 ~(1 << 2) :补码
//00000000 00000000 00000000 00000101 : 5
//00000000 00000000 00000000 00000001 : num & (~(1 << 2))
return 0;
}
4.获取num的第(n+1)位的幂值:num & (1 << n)
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//获取num第3位的幂值
int ret = num & (1 << 2);
printf("%dn", ret);
//测试案例 :5
//00000000 00000000 00000000 00000101 : 5
//00000000 00000000 00000000 00000100 : 1 << 2
//00000000 00000000 00000000 00000100 : num & (1 << 2): 4
//测试案例: 8
//00000000 00000000 00000000 00001000 : 8
//00000000 00000000 00000000 00000100 : 1 << 2
//00000000 00000000 00000000 00000000 : num & (1 << 2): 0
return 0;
}
5.将num的最高位到(n+1)位置为0:num & ((1 << n) - 1)
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//将num的最高位到第4位置为0
int ret = num & ((1 << 3) - 1);
printf("%dn", ret);
//测试案例:121
//00000000 00000000 00000000 01111001 :121
//00000000 00000000 00000000 00000111 : 1 << 3 - 1
//00000000 00000000 00000000 00000001 : 7
return 0;
}
6.将num的第1位到第(n+1)位置为0:num & (~((1 << (n + 1)) - 1))
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//将num的第0位到第3位置为0
int ret = num & (~((1 << (2 + 1)) - 1));
printf("%dn", ret);
//测试案例: 121
//00000000 00000000 00000000 00000111 :(1 << (2 +1) - 1)
//11111111 11111111 11111111 11111000 :~(1 << (2 +1) - 1)
//00000000 00000000 00000000 01111001 :121
//00000000 00000000 00000000 01111000
return 0;
}
7.代替减法进行两数相减:x + ~y + 1
#include <stdio.h>
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int ret = x + ~y + 1;
printf("%d - %d = %dn", x, y, ret);
//测试案例:x = 5, y = 3
//00000000 00000000 00000000 00000011 y
//11111111 11111111 11111111 11111100 ~y
//00000000 00000000 00000000 00000101 x
//00000000 00000000 00000000 00000001 x + ~y = 1
//00000000 00000000 00000000 00000010 x + ~y + 1 = 2
//测试案例: x = 3, y = 5
//00000000 00000000 00000000 00000101 y
//11111111 11111111 11111111 11111010 ~y
//00000000 00000000 00000000 00000011 x
//11111111 11111111 11111111 11111101 x + ~y
//11111111 11111111 11111111 11111110 x + ~y + 1 补码
//11111111 11111111 11111111 11111101 反码
//10000000 00000000 00000000 00000010 原码 -2
return 0;
}
8.代替加法进行两数相加:x - ~y - 1
#include <stdio.h>
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int sum = ~y;
int ret = x - ~y - 1;
printf("%d + %d = %dn", x, y, ret);
//测试案例:x = 5, y = 3
//00000000 00000000 00000000 00000011 y
//00000000 00000000 00000000 00000101 x
//11111111 11111111 11111111 11111100 ~y
//00000000 00000000 00000000 00001001 x - ~y
//00000000 00000000 00000000 00001000 x - ~y - 1: 8
return 0;
}
注:位运算的基本常用的操作就在这点了,但是远不止这点,需要我们更加努力的学习和探索位运算的奥妙。
注:若是觉得CSDN看起来很难受的话,请去有道云看我写的原文(排版更好)
链接在此:有道云笔记