当前位置: 首页 > news >正文

算法之位运算

前言

位运算在我们的学习中占有很重要的地位,从二进制中数的存储等都需要我们进行位运算

一、位运算复习

1.位运算复习

按位与(&):如果两个相应的二进制位都为1,则该位的结果值才为1,否则为0
按位或( | ):如果两个相应的二进制位中有一个为1,则该位的结果值为1,否则为0
按位异或( ^ ):如果两个相应的二进制位值相同,则该位的结果值为0,否则为1
按位取反( ~ ):对二进制数按位取反,即0变为1,1变为0
在这里插入图片描述
其中我们使用最多的为异或操作, 异或操作又可以称为无进位相加。
在这里插入图片描述

2.常见的位操作

1.异或的运算规则

1. 两个相同的数异或结果为0
2. 异或满足交换律
3. 任何数异或0都为那个数本身
在这里插入图片描述

2.获得一个数的相反数

这个数取反加1为这个数的相反数,即( -num = (~num + 1) )
在这里插入图片描述

3.获得一个数中二进制位最右侧的1

这个数和它的相反数按位与,即( num &= -num)
在这里插入图片描述
其他的操作我们在下面补充

二、位运算练习

1.不创建第三变量进行交换两个数的值

void Swap()
{int a = 10;int b = 20;printf("交换前:a = %d,b = %d\n", a, b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交换后:a = %d,b = %d\n", a, b);
}

在这里插入图片描述
此时用的是相同的数异或为0,0异或任何数都为那个数
注意,异或操作值可以相同,但不可以对同一块空间的值进行异或,否则会变为0,比如同时对a进行异或。
测试结果:
在这里插入图片描述
相关练习

2.不使用比较操作返回较大的数

void Compare()
{int a = 10;int b = 20;int c = a - b;int sya = ((a >> 31) & 1);//获得a的符号位int syb = ((b >> 31) & 1);//获得b的符号位int syc = ((c >> 31) & 1);//获得c的符号位int dif = sya ^ syb;//判断a和b的符号位是否相同//符号位相同则不会产生溢出if (dif == 0){if (syc == 1){printf("最大值为:%d\n", b);}else{printf("最大值为:%d\n", a);}}//可能会产生溢出else{if (sya == 1 && syb == 0){printf("最大值为:%d\n", b);}else{printf("最大值为:%d\n", a);}}
}

测试结果:
在这里插入图片描述

注意,两个数相加或者相减都可能产生溢出的情况,下面以char为例:
在这里插入图片描述
又比如:
在这里插入图片描述
所以这个需要我们进行判断是否会进行溢出,这个直接返回两个数相减的结果是否大于0在溢出情况下并不适用。
练习操作

3.不使用加减乘除等符号进行相应的操作

1.加法

//加法
int Add(int a, int b)
{int sum = a;while (b != 0){sum = a ^ b;//获得要进行进位的位置b = (a & b) << 1;a = sum;}
}

在这里插入图片描述
测试结果:
在这里插入图片描述
我们异或是不进位相加,所以下面两个数进行与是找到这两数都为1的位置,进行右移在异或相当于向前进1。

2.减法

//减法
int Sub(int a, int b)
{//获得b的相反数int sum = Add(~b, 1);//a和b的相反数进行相加sum = Add(a, sum);return sum;
}

测试结果:
在这里插入图片描述
在这里我们可以同通过我们的add函数来得到我们的所要的相反数,原理就是我们的按位取反加1得到

3.乘法

//乘法
int Mul(int a, int b)
{int sum = 0;while (b != 0){if ((b & 1) != 0)//b当前最右侧的是否为1{sum = Add(sum, a);}//模拟相乘的过程a <<= 1;//a进行右移b >>= 1;//b进行左移}return sum;
}

测试结果:
在这里插入图片描述
原理:二进制的乘法和十进制的乘法相同,在这里我们把十进制相乘转换为二进制相乘,我们可以通过移位来获得每个相加的数
在这里插入图片描述

4.除法

//除法
int div(int a, int b)
{int x = a < 0 ? Add(~a, 1) : a;int y = b < 0 ? Add(~b, 1) : b;int sum = 0;int i = 0;//从第31位开始进行判断for (i = 31; i > -1; i = Sub(i, 1)){if ((x >> i) >= y){sum |= (1 << i);x = Sub(x, y << i);}}return ((a < 0) ^ (b < 0)) ? Add(~sum, 1) : sum;
}
int Div(int a, int b)
{if (b == 0){//0为除数,结束程序exit(-1);}if (a == INT_MIN && b == INT_MIN){return 1;}else if (b == INT_MIN){return 0;}else if (a == INT_MIN){int sum = div(Add(a, 1), b);sum = Add(sum, div(Sub(a, Mul(sum, b)), b));return sum;}else{return div(a, b);}
}

测试结果:
在这里插入图片描述
原理:
在这里插入图片描述
注意:移动时要注意溢出情况,和除数是否为0。

4.整数二进制中1的个数

void Sum1()
{int a = 20;int sum = 0;while (a != 0){a &= (a - 1);sum++;}printf("1的个数为%d个\n", sum);
}

在这里插入图片描述
从上面可以看出我们的( a &= (a - 1) )操作是为了消除1
测试结果:
在这里插入图片描述
题目练习

5.在其他数都出现两次的数组中找出现一次的一个数

void Single_num()
{int arr[] = { 1,1,2,2,3,4,4,5,5,6,6 };int sum = 0;int i = 0;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){sum ^= arr[i];}printf("出现一次的数%d\n", sum);
}

这个就是我们使用的交换律,和两个相同的数异或结果为0
测试结果:
在这里插入图片描述
练习题目

6.在其他数都出现两次的数组中找出现一次的两个数

void two_Single_num()
{int arr[] = { 1,1,2,2,3,4,5,5,6,6 };int sum = 0;int i = 0;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){//找到这两个出现一次的数sum ^= arr[i];}//获得这两数异或结果最右侧的1,这个1也是这两个数不相同的位置int sum1 = sum & (~sum + 1);int sum2 = 0;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){//&的优先级低于 ==//把和这个不相同的一位的值进行按位与,如果结果为0;则可以分为1组if ((sum1 & arr[i]) == 0){//得到的为其中的一位sum2 ^= arr[i];}}printf("出现一次的数%d %d\n", sum^sum2,sum2);
}

测试结果:
在这里插入图片描述
在这里插入图片描述
题目练习

7.在其他数都出现K次的数组中找出现一次的数

void k_Single_num()
{int arr[] = { 5, 4, 1, 1, 5, 1, 5 };int k = 3; int x = 0;int a[32] = { 0 };for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){//把数组中的数的每个进制位都进行相加x = arr[i];for (int j = 31; j >= 0; j--) {if (x & 1)a[j]++;x >>= 1;}}x = 0;for (int i = 0; i < 32; i++) {//对我们相加的值的每一个进制位进行相应的取模运算就是我们所要的值了x <<= 1;x += a[i] % k;}printf("%d\n", x);
}

测试结果:
在这里插入图片描述
原理:
在这里插入图片描述
我们可以看到在我们第i位置的值为((a[i] + b[i]) % 5),同理,在K进制中的两个数在i位上的无进位相加结果位((a[i] + b[i]) % K),所以k个相同的K进制位相加,那么相加后的结果每一位都为0。所以我们把出现的次数设置成相应的K进制位,最后把相加的结果转化为10进制就是我们所需要的值。

题目练习

总结

位运算还有许多的题目,大家可以在下面找着练习一下。

http://www.lryc.cn/news/161130.html

相关文章:

  • flask使用Flask-Mail实现邮件发送
  • React refers to UMD global, but the current file is a module vite初始化react项目
  • vscode 调试 ROS2
  • TuyaOS开发学习笔记(2)——NB-IoT开发SDK架构、运行流程
  • Qt应用开发(基础篇)——普通按钮类 QPushButton QCommandLinkButton
  • Data Structures Fan(cf)
  • BIOS < UEFI
  • 微信最新更新隐私策略(2023-08-15)
  • Java中xml转javaBean
  • Spring Boot集成JPA和ClickHouse数据库
  • Hadoop生态圈中的Hive数据仓库技术
  • idea配置gitLab
  • 工程可以编译通过,但是Vscode依然有波浪线提示
  • 黑马JVM总结(二)
  • 《Effective C++中文版,第三版》读书笔记7
  • 脚本:python实现动态爱心
  • 【李宏毅】深度学习6:机器学习任务攻略
  • 如何使用SQL SERVER的OpenQuery
  • element-tree树结构-默认选中第一个节点高亮-根据id选中节点高亮
  • Python实操 PDF自动识别并提取Excel文件
  • JVM监控和调优常用命令jps|jstat|jinfo|jmap|jhat|jstack实战
  • chatglm2-6b在P40上做LORA微调 | 京东云技术团队
  • WebGL 同时使用多幅纹理
  • 探索云计算和大数据分析的崛起:API行业的机遇与挑战【电商大数据与电商API接入】
  • android studio通过wifi、无线连接设备
  • kafka 3.5 主题分区ISR伸缩源码
  • 1-centOS7搭建伪分布式
  • 对开源自动化测试平台MeterSphere的使用感触
  • Spring boot 第一个程序
  • 【SpringMVC】实现增删改查(附源码)