【算法】刷题中的位运算

news2024/9/29 23:33:51

作者:指针不指南吗
专栏:算法篇

🐾人类做题的过程,其实是暴搜的过程🐾

文章目录

  • 1.位运算概述
  • 2.位运算符
  • 3.位运算应用
    • 3.1整数的奇偶性判断
    • 3.2有关 2 的幂的应用
    • 3.3lowbit(x)返回x的最后一位1
    • 3.4二进制数中1的个数
    • 3.5求二进制位的某一位是几
    • 3.6交换两个整型变量的值
    • 3.7数组中x出现的次数
    • 3.8快速幂取模
  • 4.位运算总结

简单介绍一下位运算概念,更多的是写位运算在刷题过程中的应用

1.位运算概述

​ 位运算就是直接对整数在内存中的二进制位进行操作,由于计算机内部就是以二进制来存储数据,位运算是相当快的。

​ 基本的位运算共 6种,分别为按位与、按位或、按位异或、按位取反、左移和右移

​ 位运算一般有三种作用:

  1. 高效地进行某些运算,代替其它低效的方式。
  2. 表示集合。(常用于状压DP )
  3. 题目本来就要求进行位运算。

2.位运算符

含义符号简述
按位与a & b同一得 1
按位或a | b有一得 1
按位异或a ^ b相同得 0
按位取反~a取反
左移a << b向左移动,低位补零,高位舍弃
带符号右移a >> b向右移动,高位补原有高位,低位舍弃
  1. 复合赋值位运算符

    += , -= 等运算符类似,位运算也有复合赋值运算符: &= , |= , ^= , <<= , >>= 。(取反是单目运算,所以没有)

  2. 数组初始化

    memset(f,0x3f,sizeof(f))

  3. 位移运算符

    左移运算符 <<
    二进制 : 1 -> 10 -> 100 -> 1000
    十进制 : 1 -> 2  -> 4   -> 8
    综上所述:1 << n  ==  2^n
    右移运算符 >>
    二进制 : 1000 -> 100 -> 10 -> 1
    十进制 :  8    -> 4   -> 2  -> 1
    综上所述: n >> x  == n / (2^x)
    
  4. 运算符优先级

    ~的优先级最高,其次是<<>>,再次是,然后是^,优先级最低的是|

​ 位运算的优先级 低于 算术运算符(除了取反),而按位与、按位或及异或 低于 比较运算符(详见 运算页面 ),所以使用时需多加注意,在必要时添加括号。

3.位运算应用

3.1整数的奇偶性判断

  • 朴素做法

    if(a%2==1)
        //为奇数
    else
        //为偶数
    
  • 按位与 -> 二进制的末位为0表示偶数,最末位为1表示奇数

    if(a & 1 != 1)
        //为奇数
    else
        //为偶数
    

3.2有关 2 的幂的应用

将一个数乘(除) 2 的非负整数次幂

// 计算 n*(2^m)
int mulPowerOfTwo(int n, int m)   
{
  return n << m;
}

// 计算 n/(2^m)
int divPowerOfTwo(int n, int m)   
{
  return n >> m;
}

判断一个数是否是2的幂次方,若是,并判断出来是多少次方

题目链接: 力扣 231. 2的幂

​ 将2的幂次方写成二进制形式后,很容易就会发现有一个特点:二进制中只有一个1,并且1后面跟了n个0; 因此问题可以转化为判断1后面是否跟了n个0就可以了

如果将这个数减去1后会发现,仅有的那个1会变为0,而原来的那n个0会变为1;因此将原来的数与去减去1后的数字进行与运算后会发现为零。

   最快速的方法:

  (number & number - 1) == 0

  原因:因为2的N次方换算是二进制为10……0这样的形式(0除外)。按位与上自己-1的位数,这们得到结果为0。例如,8的二进制为1000;8-1=7,7的二进制为111。两者相与的结果为0。计算如下:
     1000
   & 0111
   -------
     0000

代码实现如下:

#include<bits/stdc++.h>
using namespace std;
 
//判断一个数是2的多少次方
int log2(int value)   
{
	int x=0;
	while(value>1)
	{
		value>>=1;
		x++;
	}
	return x;
}
 
int main()
{
	int num;
	scanf("%d",&num);
    
    //使用与运算判断一个数是否是2的幂次方
	if(num&(num-1))     
		printf("%d不是2的幂次方!\n",num);
	else
		printf("%d是2的%d次方!\n",num,log2(num));
    
	return 0;
}

3.3lowbit(x)返回x的最后一位1

​ lowbit(x):返回x的最后一位1,即一个二进制最低位的1与后边的0组成的数。

​ x = 1010 lowbit(x) = 10

​ x= 101000 lowbit(x) = 1000

​ 实现原理:x & -x = x & (~x + 1),负数的补码:原码取反加一(利用了负整数的补码特性)

3.4二进制数中1的个数

题目链接:力扣 191.位1的个数

  • 朴素做法 -> 使用移位操作,判末位是否为1;移位的次数为32

    int BitCount(unsigned int n)
    {
        unsigned int c =0 ; // 计数器
        while (n >0)
        {
            if((n &1) ==1) // 当前位是1
                ++c ; // 计数器加1
            n >>=1 ; // 移位
        }
        return c ;
    }
    
  • 快速做法 -> 迭代n=n&(n-1),消除最右边的1,计数

    int BitCount2(unsigned int n)
    {
        unsigned int c =0 ;
        for (c =0; n; ++c)
        {
            n &= (n -1) ; // 清除最低位的1
        }
        return c ;
    }
    

3.5求二进制位的某一位是几

n 的二进制中第 k 位数字

  • 先把第k为移到最后一位 n>>k

  • 看个位是几 x&1

把上面两步综合 即 n>>k&1

应用:输出n=10的二进制

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n=10;
    
    for(int k=3;k>=0;k--) //从0位开始的(右到左)
        cout<<(n>>k&1);
    
    return 0;
}

3.6交换两个整型变量的值

异或的性质:

1.交换律:可任意交换运算因子的位置,结果不变;

如:a ^ b ^ c = b ^ a ^ c;

2.结合律:即(a ^ b) ^ c == a ^ ( b ^ c) ;

3.对于任何数x, 都有x ^ x = 0, x ^ 0 = x,同自己求异或为0,同0求异或为自己

4.自反性:A ^ B ^ B = A ^ 0 = A, 连续和同一个因子做异或运算,最终结果为自己

例题:int A = 10, int B = 20, 在不引入第3个变量的情况下,交换两个变量的值。

异或法——代码实现

#include<bits/stdc++.h>
using namespace std;

int main()
{
	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);
    return 0;	
}

3.7数组中x出现的次数

应用一:数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数
题目链接:力扣 136.只出现一次的数字 |

因为只有一个数恰好出现一个,剩下的都出现过两次,所以只要将所有的数异或起来,就可以得到唯一的那个数,因为相同的数出现的两次,异或两次等价于没有任何操作。

代码实现

int singleNumber(int nums[]) 
{
    int result = 0, n = sizeof(nums)/sizeof(nums[0]);

    for (int i = 0; i < n; i++)
    {
        result ^= nums[i];
    }
    return result;
}

应用二:数组中,只有一个数出现一次,剩下都出现三次,找出出现一次的数

题目链接:力扣 137.只出现一次的数字||

为了方便叙述,我们称「只出现了一次的元素」为「答案」。

由于数组中的元素都在 int(即 32 位整数)范围内,因此我们可以依次计算答案的每一个二进制位是 0还是1。具体地,考虑答案的第 i 个二进制位(i 从0开始编号),他可能为0或者1。对于数组中非答案的元素,每个元素都出现了3次,3次对应第i个二进制位和的3个0或者3个1,无论哪一种情况,他的结果相加(0或者3)都是3的倍数,答案的第 i 个二进制位就是数组中所有元素的第 i 个二进制位之和除以 3 的余数。

这样一来,对于数组中的每一个元素 x,我们使用位运算 (x>>i)&1 得到 x 的第 i 个二进制位,并将它们相加再对 3 取余,得到的结果一定为 0 或 1,即为答案的第 i 个二进制位

代码实现

int singleNumber(vector<int>& nums) 
{
    int ans = 0;
    for (int i = 0; i < 32; ++i) {
        int total = 0;
        for (int num: nums) {
            total += ((num >> i) & 1);
        }
        if (total % 3) {
            ans |= (1 << i);
        }
    }
    return ans;
}

针对上面进行拓展,如果是数组中,只有一个数出现一次,剩下都出现 k 次 ,找出出现一次的数呢

total % k  //将3改为 k ,对 k 进行取模即可 

应用三:如何找数组中唯一成对的那个数

1-10这10个数放在含有11个元素的数组中,只有唯一一个元素重复,其他均只出现一次,要求每个数组元素只能够被访问一次,请设计一个算法,将它找出来 。

位运算中 异或 ^ 的特点,A^A=0 A^0=A ,也就是说,两个相同的数字进行异或结果为0,可以用来消除重复。 可惜,题目要求寻找重复的值,所以,我们对这1001个数字 加上(1 ~ 1000)这1000个数字,这样1~1000所有的数字出现了2次,可以消除,而那个重复的数字由于加了一次,变成了3次,A ^ A ^ A =A。从而得出那个重复的A。

代码实现

int findDouble(int T[])
{
    int res=0; //定义一个返回结果,初始值为0,因为A^0=A
    
    //先对T数组进行异或
    for(int i=0;i<T.length;i++)
    {
        res^=T[i];
    }
    
    //在与1~1000异或
    for(int i=1;i<=1000;i++)
    {
        res^=i;
    }
    return res;
}

3.8快速幂取模

给你三个整数 a,b,p,求 a b m o d p a^ b mod p abmodp
题目链接:P1226 【模板】快速幂 | 取余运算

取平方思路

参考文章:https://oi-wiki.org/math/binary-exponentiation/

先看这个式子 a 2 b = a 2 ∗ a b a^{2b}=a^2*a^b a2b=a2ab ,我们发现取平方可以缩短计算次数,我们可以按照 二进制 来表示幂。那我们来看看幂和二进制之间的关系。

举个例子讲解:例如: 3 13 = 3 ( 1101 ) 2 = 3 8 ∗ 3 4 ∗ 3 1 3^{13}=3^{(1101)_2}=3^8*3^4*3^1 313=3(1101)2=383431

是不是发现,这里面只有二进制位是1的才乘到里面,是0的跳过,所以我们只需要用10进制转2进制的方法(不断÷2的余数,直到商为0),即可得到幂数对应的二进制数。**如果某一个二进制位是1,那就将对应的数乘到结果里面,并且底数也翻倍;如果是0,则底数也翻倍。**可看下面的推导过程,这个地方有点绕,跟着过一遍就懂了。

取模定理

(a * b) % p = (a % p * b % p) % p (3

乘积的取模等于各个因子取模相乘然后再取模;

取模的运算不会干涉乘法运算,因此我们只需要在计算的过程中取模即可 。

快速幂代码实现

long long binpow(long long a, long long b) 
{
    long long res = 1;
    while (b > 0) 
    {
        if (b & 1) res = res * a;
        a = a * a;
        b >>= 1;
  	}
  	return res;
}

快速幂取模代码实现

long long binpow(long long a, long long b, long long m) 
{
	a %= m;
    long long res = 1;
    while (b > 0) 
    {
        if (b & 1) res = res * a % m;
        a = a * a % m;
        b >>= 1;
  	}
  	return res;
}

4.位运算总结

​ 在刷题中,位运算是一个非常常见的技巧和思路。它能够在一定程度上优化时间和空间复杂度,使得程序更加高效。

​ 在一些需要对二进制进行操作的场景中,位运算能够帮助我们更好地处理问题。比本文介绍的在统计一个数的二进制中有几个1的问题、判断一个数是否是2的幂次方、交换两个整型变量的值等等。

Alt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6.S081——陷阱部分(一篇读懂Xv6系统调用)——xv6源码完全解析系列(5)

0.briefly speaking 这篇博客将要开始尝试阅读和研究与Xv6陷阱机制相关的代码&#xff0c;主要有以下文件&#xff0c;最重要的是结合Xv6 book将Xv6处理陷阱的相关逻辑和流程弄透。在Xv6的语境中所谓陷阱的触发有以下三种情况&#xff1a; 系统调用严重错误&#xff08;比如除…

设备仪器仪表盘读数识别算法 yolov5

设备仪器仪表盘读数识别系统基于YoLov5网络模型分析技术&#xff0c;设备仪器仪表盘读数识别算法模型自动识别指针型仪表读数。Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测&#xff0c;核心思想就是利用整张图作为网络的输入&#xff0c;直接在输出层回归 bounding…

【Maven笔记2】Maven安装与配置

1、前置准备 maven是使用Java语言进行开发的&#xff0c;因此在安装maven之前&#xff0c;先需要有java运行环境。如何确认本机是否安装了JDK环境呢&#xff1f;打开终端运行如下命令&#xff1a; java -version如下图显示说明已经安装了JDK环境。 备注&#xff1a;如何安装J…

Intel Xeon(Ice Lake) Platinum 8369B阿里云CPU处理器

阿里云服务器CPU处理器Intel Xeon(Ice Lake) Platinum 8369B&#xff0c;基频2.7 GHz&#xff0c;全核睿频3.5 GHz&#xff0c;计算性能稳定。目前阿里云第七代云服务器ECS计算型c7、ECS通用型g7、内存型r7等规格均采用该款CPU。 Intel Xeon(Ice Lake) Platinum 8369B Intel …

OSI七层网络参考模型

七层模型的诞生 深夜中&#xff0c;在一家美国酒吧坐着几个正在谈论迪斯尼电影里的7个小矮人&#xff0c;他们把小矮人的名字写在餐巾纸上&#xff0c;有人开玩笑说7对于网络分层是个好数字&#xff0c;这几个人就是制定OSI标准小组的成员&#xff0c;后来OSI真的就设计成了七…

TensorFlow入门图像分类-猫狗分类-MobileNet优化

在上一篇文章中《Tensorflow入门图像分类-猫狗分类-安卓》&#xff0c;介绍了使用TensorFlow训练一个猫狗图像分类器的模型并在安卓应用上使用的全过程。 在这一篇文章中&#xff0c;将采用 MobileNet 来重新训练一个猫狗图像分类器。 一、 MobileNet 介绍 MobileNet是一种轻量…

服务(第十六篇)mysql①基础

什么是数据库&#xff1f; 数据&#xff1a; ①描述事物的符号记录称为数据&#xff08;Data&#xff09;&#xff0c;数字、文字、图形、图像、声音、档案记录等&#xff1b; ②数据是以“记录”的形式按照统一的格式进行存储的&#xff0c;而不是杂乱无章的。 行&#xf…

35岁以10亿美元身价登上《财富》杂志亿万富豪榜的电商传奇谢家华

Zappos的介绍 Zappos可谓是电商的传奇&#xff0c;国内同类电商是乐淘。Zappos是一家在线卖鞋和服装的公司&#xff0c;1999年创立&#xff0c;2009年被亚马逊以12亿元收购&#xff0c; 多次入选财富杂志最佳雇主公司top100。 Zappos的创始人及CEO 提到Zappos就不得不介绍下…

SQL知识汇总

什么时候用存储过程合适 当一个事务涉及到多个SQL语句时或者涉及到对多个表的操作时就要考虑用存储过程&#xff1b;当在一个事务的完成需要很复杂的商业逻辑时&#xff08;比如&#xff0c;对多个数据的操作&#xff0c;对多个状态的判断更改等&#xff09;要考虑&#xff1b…

05.rabbitMQ之搭建的各种坑

1.持久化需要重新设置队列 2.异步发布确认的坑, 生产者发消息太快只会确认最大的编号 1.消费者还是要确认消息 channel.basicAck(message.getEnvelope().getDeliveryTag(), false); 因为你发送的太快了&#xff0c;只会返回成功接收的最大的编号 3.消费者消息堆积(开启了消息手…

InnoDB 磁盘结构之数据字典和双写缓冲区

数据字典&#xff08;InnoDB Data Dictionary&#xff09; MySQL中&#xff0c;数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成&#xff0c;这些表包含用于查找表…

7万字水利数字孪生工程解决方案(word可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 1.1 系统开发方案 1.1.1 系统设计开发思路 &#xff08;1&#xff09;基于层次分解的设计 xxx水利数字孪生工程将采用基于层次分解的系统模型,系统采用这种方式进行层次划…

【P5】JMeter CSV Data Set Config(CSV 数据文件设置)

文章目录 一、测试计划演示二、CSV Data Set Config&#xff08;CSV 数据文件设置&#xff09;主要参数说明2.1、忽略首行&#xff1a;True2.2、是否允许带引号&#xff1f;&#xff1a;False2.3、遇到文件结束符再次循环&#xff1f;&#xff1a;False2.4、遇到文件结束符停止…

Apache 可能会出手接盘 Google Wave

尽管Google计划在明年终止Google Wave项目&#xff0c;但他们提供Wave in a Box开源项目允许你在自己的服务器上跑一个Google Wave服务玩。据The Register报道&#xff0c;Apache Software Group正在试图将Wave in a Box移植到目前的管理系统里。尽管目前还处于早期孵化阶段&am…

AI模型部署概述

心口如一&#xff0c;犹不失为光明磊落丈夫之行也。——梁启超 文章目录 :smirk:1. AI模型部署方法:blush:2. AI模型部署框架ONNXNCNNOpenVINOTensorRTMediapipe如何选择 :satisfied:3. AI模型部署平台 &#x1f60f;1. AI模型部署方法 在AI深度学习模型的训练中&#xff0c;…

链游“风暴之年”已来 一文解读Web3游戏的前生今世

链上世界进入游戏市场&#xff0c;让越来越多游戏厂商不由得感叹区块链游戏&#xff08;简称“链游”&#xff09;的风暴之年正在加速到来。如今&#xff0c;游戏活动转变了单一的休闲娱乐理念&#xff0c;逐渐走向Web3发展个性化、可定义的未来。 前不久&#xff0c;阿里云作为…

S3C6410 中的 irqdomain 之 gpio

文章目录 VIC domain 与 gpio domain 的硬件拓扑图描述linux cascaded irq domainlinux irq domain 实例VIC domain 与 gpio domain 的硬件拓扑语言描述VIC 与 INT_EINTx 的关系INT_EINTx 与 GPIO的关系INT_EINT0INT_EINT1INT_EINT2INT_EINT3INT_EINT4INT_EINT4 与 External in…

【Elasticsearch】DSL操作相关

文章目录 DSL操作索引操作新建索引查询索引查看所有索引删除索引 映射操作创建映射查看映射索引映射关联(同创建映射类似) 文档操作创建文档查询指定ID文档查询所有文档全局修改文档局部修改文档删除文档条件删除 数据搜索数据准备条件查询(match)多字段条件查询(multi_match)关…

VMware 虚拟机中 Linux 系统Centos7磁盘空间扩容(亲测)

1.修改虚拟机磁盘容量 例如之前虚拟机磁盘空间为30G&#xff0c;现要将磁盘容量设置为50G 打开虚拟机&#xff08;必须处于关机状态&#xff09;&#xff0c;点击【编辑虚拟机设置】&#xff0c;然后点击【磁盘】&#xff0c;接着点击【扩展】&#xff0c;输入修改后的最大磁盘…

LangChain入门(二)-通过 Google 搜索并返回答案

GitHub - liaokongVFX/LangChain-Chinese-Getting-Started-Guide: LangChain 的中文入门教程LangChain 的中文入门教程. Contribute to liaokongVFX/LangChain-Chinese-Getting-Started-Guide development by creating an account on GitHub.https://github.com/liaokongVFX/La…