【优选算法】位运算 {位运算符及其优先级;位运算的应用:判断位,打开位,关闭位,转置位,位图,get lowbit,close lowbit;相关编程题解析}

news2024/12/25 0:34:07

一、位运算符及其优先级

我们知道,计算机中的数在内存中都是以二进制形式进行存储的 ,而位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。

那么,涉及位运算的运算符如下表所示:
在这里插入图片描述

注意:参与位运算的对象只能是整型数据(int, unsigned, char),不能为实型

异或运算符的运算律

  1. 异或0:a^0 = a;
  2. 消消乐:a^a = 0;
  3. 交换律和结合律:abc = acb; abc = a(bc);

位运算符的优先级

优先级需要弄清楚,如果不太清楚可以加小括号确保是想要的运算顺序,这里只是相对优先级,即只是和一些常用的算术运算符做比较。
在这里插入图片描述


二、位运算的应用

2.1 判断位

给一个数n,确定它的二进制表示中的第x位是0还是1

算法:(n>>x)&1

原理:按位与的运算规则——有0则0,遇1不变

举例:判断第3位

  • 11001010 //n
    00011001 //n>>3
    00000001 //&1
    00000001 //结果

2.2 打开位

将一个数n二进制表示的第x位修改成1

算法:n |= (1<<x)

原理:按位或的运算规则——有1则1,遇0不变

举例:打开第4位

  • 11001010 //目标
    00010000 //1<<4
    11011010 //|= 得结果

2.3 关闭位

将一个数n二进制表示的第x位修改成0

算法:n &= ~(1<<x)

原理:按位与的运算规则——有0则0,遇1不变

举例:关闭第7位

  • 11001000 //目标
    10000000 //1<<7
    01111111 //~(1<<7)
    01001000 //&= 得结果

2.4 转置位

将一个数n二进制表示的第x位转置(0变1,1变0)

算法:n ^= (1<<x)

原理:按位异或的运算规则——0变1,1变0

举例:转置第4位

  • 11011010 //目标
    00010000 //1<<4
    11001010 //^= 得结果

2.5 位图

在这里插入图片描述

定义和特性:位图是一种数据结构,它由一个二进制位数组或者比特数组组成,每个位(bit)只能存储 0 或 1。位图通常被用来表示大量整数的集合,通过位的状态来表示该整数是否出现在集合中。位图支持高效的位操作(如按位与、按位或、按位异或等),适合用于高效地存储和操作大量的布尔型数据。

实现方式:位图可以被实现为一个数组,其中每个元素都是一个字节,而每个字节又包含8个位。对于很大的位图,可以使用多个数组进行分段存储。位图通常不仅仅用于表示整数的集合,还可以用于标记某个特定值是否存在。

详细内容请阅读文章:【高阶数据结构】哈希的其他应用 {位图的介绍及实现,位图的组合应用;布隆过滤器的介绍及实现,布隆过滤器的应用;哈希切分}-CSDN博客


2.6 get lowbit

提取一个数n二进制表示中最低位的1

算法:n & -n

原理:-n(按位取反再加1)将最低位的1左边的位(不包括logbit位)全部转置,左边转置后按位与得0,右边相同位按位与不变。

举例:

  • 11011000 //目标
    00101000 //-n
    00001000 //& 得结果

2.7 close lowbit

关闭一个数n二进制表示中最低位的1

算法:n & (n-1)

原理:n-1 将最低位的1右边的位(包括logbit位)全部转置,左边相同位按位与不变,右边转置后按位与得0。

举例:

  • 11011000 //目标
    11010111 //n-1
    11010000 //& 得结果

2.8 其他

  • 位运算实现乘除法:将a左移一位实现*2,将a右移一位实现/2。

    • a < < n ≡ a ∗ 2^n
    • a > > n ≡ a / 2^n
  • 统计二进制数中有多少个1:

    int func(int x){
    	int count=0;
    	while (x)
    	{
    		count++;
    		x=x&(x-1); //将lowbit关闭
    	}
    	return count;
    }
    
  • 字符的大小写转换:对应大小写字母ASSIC码的二进制数只有第5位不同(大写为0,小写为1),因此可以通过操作第5位实现大小的相互转换

    • 字符小写转大写:ch&=~32;
    • 字符大写转小写:ch|=32;

    • 字符大小写互换:ch^=32;

  • 交换两变量的值:a=a^b; b=a^b; a=a^b;(异或运算符的运算律:结合律、消消乐)


三、相关编程题

3.1 位1的个数

题目链接

191. 位1的个数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

不断的close lowbit,每关闭一个1count就++,直到将所有的1都关闭,量的值变为0。

编写代码

class Solution {
public:
    int hammingWeight(int n) {
        int count = 0;
        while(n>0)
        {
            n &= (n-1);
            ++count;
        }
        return count;
    }
};

3.2 比特位计数

题目链接

338. 比特位计数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

同上 close lowbit

编写代码

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans(n+1, 0);
        for(int i = 0; i <= n; ++i)
        {
            int count = 0;
            int k = i;
            while(k>0)
            {
                ++count;
                k &= k-1;
            }
            ans[i] = count;
        }
        return ans;
    }
};

3.3 汉明距离

题目链接

461. 汉明距离 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

异或运算:相异为1,相同为0。tmp = x^y将x和y二进制表示中不同的位置置为1,相同的位置置为0。再统计tmp中位1的个数即可,原理同上 close lowbit

编写代码

class Solution {
public:
    int hammingDistance(int x, int y) {
        int tmp = x ^ y;
        int count = 0;
        while(tmp>0)
        {
            ++count;
            tmp &= tmp-1;
        }
        return count;
    }
};

3.4 只出现一次的数字

题目链接

136. 只出现一次的数字 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

异或运算的运算律:消消乐

编写代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(auto e : nums)
        {
            ret ^= e;
        }
        return ret;
    }
};

3.5 只出现一次的数Ⅲ

题目链接

260. 只出现一次的数字 III - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

  1. 将所有的数按位异或到tmp,最终其实就是只出现一次的两个数的异或结果,即将两个数不同的位置置为1。
  2. get lowbit将tmp的最低位1取出(还是存入tmp)。注意有符号数需要特殊处理INT_MIN(符号位为1,其他位为0),有符号数取反的位操作是:符号位不变,其他位取反再+1。INT_MIN取反会溢出。实际上INT_MIN的lowbit就是它本身。
  3. 再次遍历数组,判断所有数的tmp位(异或结果的最低位1),将只出现一次的两个数分到不同的组中进行异或,最终就能分别得到这两个数了。

编写代码

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int tmp = 0;
        for(auto e : nums)
        {
            tmp ^= e;
        }
        tmp = tmp==INT_MIN? tmp:tmp&(-tmp); //get lowbit
        vector<int> ret(2, 0);
        for(auto e : nums)
        {
            if(e & tmp)
            {
                ret[0] ^= e;
            }
            else
            {
                ret[1] ^= e;
            }
        }
        return ret;
    }
};

3.6 判定字符是否唯一

题目链接

面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

//用一个int数据充当位图
class Solution {
public:
    bool isUnique(string astr) {
        // 利用鸽巢原理进行优化
        if(astr.size() > 26)
            return false;
        // 用位图判断一个字符是否在集合中
        int bitmap = 0;
        for(auto e : astr)
        {
            int tmp = 1 << (e-'a');
            if(!(bitmap & tmp))
            {
                bitmap |= tmp;
            }
            else
            {
                return false;
            }
        }
        return true;
    }
};

//用STL的bitset数据结构
class Solution {
public:
    bool isUnique(string astr) {
        if(astr.size() > 26)
            return false;
        bitset<26> bs;
        for(auto e : astr)
        {
            int x = e - 'a'; //位图的第x位
            if(!bs.test(x))
            {
                bs.set(x);
            }
            else
            {
                return false;
            }
        }
        return true;
    }
};

3.7 丢失的数字

题目链接

268. 丢失的数字 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int ret = 0;
        int i = 0;
        for(; i < nums.size(); ++i)
        {
            ret ^= nums[i];
            ret ^= i;
        }
        ret ^= i;
        return ret;
    }
};

3.8 两整数之和

题目链接

371. 两整数之和 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int getSum(int a, int b) {
        int bitsum = a ^ b; //无进位相加的结果
        int bitcarry = (a & b)<<1; //进位的结果
        while(bitcarry != 0) //当不再有进位时退出循环
        {
            a = bitsum;
            b = bitcarry;
            bitsum = a ^ b;
            bitcarry = (a & b)<<1;
        }
        return bitsum;
    }   
};

3.9 只出现一次的数字Ⅱ

题目链接

137. 只出现一次的数字 II - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

依次确定每一个二进制位

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

由于数组中的元素都在 int(即 32 位整数)范围内,因此我们可以依次计算答案的每一个二进制位是 0 还是 1。

具体地,考虑答案的第 i 个二进制位(i 从 0 开始编号),它可能为 0 或 1。对于数组中非答案的元素,每一个元素都出现了 3 次,对应着第 i 个二进制位的 3 个 0 或 3 个 1,无论是哪一种情况,它们的和都是 3 的倍数(即和为 0 或 3)。因此:

答案的第 i 个二进制位就是数组中所有元素的第 i 个二进制位之和除以 3 的余数。

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

编写代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(size_t i = 0; i < 32; ++i)
        {
            int bitsum = 0;
            for(auto e : nums)
            {
                bitsum+=(e>>i)&1;
            }
            bitsum %= 3;
            ret |= (bitsum<<i);
        }
        return ret;
    }
};

3.10 消失的两个数字

题目链接

面试题 17.19. 消失的两个数字 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int tmp = 0;
        //异或到一起
        for(auto e : nums) tmp ^= e; //原数组
        for(int i = 1; i<=nums.size()+2; ++i) tmp ^= i; //全数组
        //此时的tmp种存放的是消失的两个数字的异或结果
        int lowbit = tmp==INT_MIN? tmp:tmp&(-tmp);

        vector<int> ret(2, 0);
        //划分成两类异或
        for(auto e : nums) //原数组
            if(e & lowbit) ret[0] ^= e;
            else ret[1] ^= e;
        for(int i = 1; i<=nums.size()+2; ++i) //全数组
            if(i & lowbit) ret[0] ^= i;
            else ret[1] ^= i; 
        return ret;
    }
};

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

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

相关文章

SpringBoot 上传文件示例

示例效果&#xff1a; 前端代码&#xff1a; <html> <head><title>上传文件示例</title></head> <body> <h2>方式一&#xff1a;普通表单上传</h2> <form action"/admin/upload" method"post" enctyp…

【Python】 Django 框架如何支持百万级日访问量

基本原理 Django 是一个高级的 Python Web 框架&#xff0c;它鼓励快速开发和干净、实用的设计。Django 遵循 MVC&#xff08;模型-视图-控制器&#xff09;设计模式&#xff0c;允许开发者通过编写更少的代码来构建高质量的 Web 应用程序。Django 自带了许多内置功能&#xf…

Python-opencv通过距离变换提取图像骨骼

文章目录 距离变换distanceTransform函数 距离变换 如果把二值图像理解成地形&#xff0c;黑色表示海洋&#xff0c;白色表示陆地&#xff0c;那么陆地上任意一点&#xff0c;到海洋都有一个最近的距离&#xff0c;如下图所示&#xff0c;对于左侧二值图像来说&#xff0c;【d…

嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

golang中的md5、sha256数据加密文件md5/sha256值计算步骤和运行内存图解

在go语言中对数据计算一个md5&#xff0c;或sha256和其他语言 如java, php中的使用方式稍有不同&#xff0c; 那就是要加密的数据必须通过流的形式写入到你创建的Hash对象中。 Hash数据加密步骤 1. 先使用对应的加密算法包中的New函数创建一个Hash对象&#xff0c;(这个也就是…

linux经典定时任务

在使用时记得替换为自己的脚本路径。请在相应的脚本第一行加上#!/bin/bash&#xff0c;否则脚本在定时任务中无法执行。 1、在每天凌晨2点执行 0 2 * * * /bin/sh bashup.sh 2、每天执行两次 下面的示例命令将在每天上午5点和下午5点执行。您可以通过逗号分隔指定多个时间戳…

vue中的$nextTick和过渡与动画

一.vue中的$nextTick 简述与用法&#xff1a;这是一个生命周期钩子 1.语法&#xff1a;this.$nextTick(回调函数) 2.作用&#xff1a;在下一次DOM更新结束后执行其指定的回调 3.什么时候用&#xff1a;当修改数据后&#xff0c;要基于更新后的新dom进行某些操作时&#xff0c;…

vue.js基础组件4--下

1.动态组件 1.定义动态组件 利用动态组件可以动态切换页面中显示的组件。使用<component>标签可以定义动态组件&#xff0c;语法格式如下。 <component is"要渲染的组件"></component>上述语法格式中&#xff0c;<component>标签必须配合i…

爬山算法全解析:掌握优化技巧,攀登技术高峰!

一、引言 爬山算法是一种局部搜索算法&#xff0c;它基于当前解的邻域中进行搜索&#xff0c;通过比较当前解与邻域解的优劣来更新当前解&#xff0c;从而逐步逼近最优解。本文将对爬山算法进行详细的介绍。 二、爬山算法简介 爬山算法是一种基于贪心策略的优化算法&#xff…

Modular military character

角色具有31个模块化骨架网格,每个模块具有多个蒙皮: 3个头(4skins) 3件衬衫(9skins) 3条裤子(9skins) 3只靴子(9skins) 7件战术背心(3skins) 4只手和手臂(2skins) 3顶帽子和头盔(9skins) 2个背包(3skins) 3支步枪(3skins) 模块允许您组装超过200万个不同的…

SW手势定义

crtle:独立; T:测量;R隐藏;视图>用户界面>动态显示父子关系 crtld:相同零件; alte:草图显示; altw:基准面显示; ALTZ:上一视图;

Docker笔记-一种在非交互式方式中加载环境变量的方法

背景 我遇到的现象是这样的&#xff0c;我在docker安装了dm python的客户端&#xff0c;但dm python实际上是对libdmdpi.so的调用&#xff0c;在交互式环境中&#xff08;/bin/bash&#xff09;调用python 连接达梦是没有任何问题的&#xff0c;但在非交互环境直接调用&#x…

Mac配置node环境

1.下载nvm(node版本管理工具&#xff0c;同Anaconda对Python的关系&#xff09;。 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash 2.配置vi ~/.zshrc文件&#xff0c;添加如下配置&#xff1a; export NVM_DIR"$HOME/.nvm" [ -…

软考 系统架构设计师系列知识点之SOME/IP与DDS(1)

本文内容参考&#xff1a; 车载以太网 - SOME/IP简介_someip-CSDN博客 https://zhuanlan.zhihu.com/p/369422441 什么是SOME/IP?_someip-CSDN博客 SOME/IP 详解系列&#xff08;1&#xff09;—— 概述_some ip-CSDN博客 深入浅出SOME/IP协议&#xff1a;基本概念和原理-…

酷开科技相伴童年 | 酷开系统六一特辑:亲子共赏,启迪成长

六一儿童节&#xff0c;属于每个茁壮成长的孩子&#xff0c;也属于每个童心未泯的“少年”。《小王子》里说&#xff0c;使生活如此美丽的是我们藏起来的真诚和童心。马上就到六一儿童节了&#xff0c;就让我们用温柔而富有童真的笔触&#xff0c;唤醒那份沉睡已久的童心吧。 在…

gitlab没有合并权限、发起合并请求

1.选择项目 2.发起合并请求 3.选择需要合并的分支&#xff0c;第一个是被合并的分支&#xff0c;第二个是合并上哪个分支 4.选择有合并权限的管理人 5.发起请求&#xff0c;通知、等待管理人合并

【Docker实战】进入四大数据库的命令行模式

上一篇我们讲了docker exec命令&#xff0c;这一次我们使用docker exec命令来进入四大数据库的命令行模式。 我们进行游戏开发或软件开发是离不开四大数据库的&#xff0c;这四大数据库分别是关系型数据库mysql、postgres&#xff0c;nosql数据库redis、mongodb。将它们容器化…

GNSS的经纬度使用float还是doubble数据类型存储传输?

1. 背景 当你在使用导航、打车、定位等等场景下&#xff0c;一定会有形或者无形的使用位置服务&#xff0c;位置服务的基础功能功能就是向你提供位置信息&#xff0c;而经纬度是位置信息的主要信息&#xff0c;一般情况可以简单的认为位置信息就是经纬度信息。经纬度使用小数进…

静态随机存储器(SRAM)

目录 介绍 基本的 SRAM 存储单元阵列 1. SRAM 存储单元 2. SRAM 阵列 3. SRAM 阵列的读写操作 4. SRAM 阵列的扩展 5. SRAM 阵列的应用 6. SRAM 阵列的优缺点 基本的 SRAM 逻辑结构 1. 存储单元 2. 存储单元阵列 3. 译码器 4. 读写电路 5. 控制逻辑 6. SRAM 逻辑…

多旋翼+发电机:国债应急系留照明无人机技术详解

多旋翼发电机技术的应急系留照明无人机是一种集成了先进飞行技术、发电技术和照明技术的无人机系统。这种无人机具有高度的灵活性、移动性和适应性&#xff0c;能够在各种复杂环境下迅速部署&#xff0c;为夜间搜救、救援等应急任务提供高效、可靠的照明支持。 无人机参数&…