位运算的奇技淫巧

常见位运算总结:

1、基础位运算

左移<<运算

将二进制数向左移位操作,高位溢出则丢弃,低位补0。

右移>>运算

右移位运算中,无符号数和有符号数的运算并不相同。对于无符号数,右移之后高位补0;对于有符号数,符号位一起移动,正数高位补0,负数高位补1

按位与&运算

有0就是0,巧计:&这个符号像是有两个0组合而成。

按位或 | 运算

有1就是1,巧计:|本身就像一个1

按位异或^运算(两种解释方法)

相同为0,相异为1,或者解释成无进位相加

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

(n >> x) & 1

&1后的结果就是0/1,因为1的二进制位除了最后一位,其他都是0,&0就是0

3、将一个数n的二进制位的第x位改成1

n =(1 << x)|  n

4、将一个数n的二进制位的第x位改成0

n = n & (~(1 << x))

5、位图的思想

本质上就是一个哈希表

6、提取一个数(n)二进制表示中最右侧的1(lowbit

n & -n

解释:-n的操作就是先取反,然后再+1,这样造成的影响是最右边的1前面都是n的相反数,这样再跟原先的n&,因为是相反数,所以有一方肯定是0,这样最右边的1前面的数字都变成了0,最右边1右边本身就是0。

7、干掉一个数(n)最右边的1

n &(n - 1)

8、位运算的优先级

为了避免记住闲杂的公式,我们只需要记住能加括号就加括号。

9、异或运算的运算规律

  • a ^ 0 = a
  • a ^ a = 0(消消乐)
  • a ^ b ^ c = a ^ (b ^ c)

上面的指示是位运算的基础知识,下面就带着上面的指示开始实操啦

第一题:
191. 位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。

解析:

只需要每次右移&1判断是否为1。

原码:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int sum = 0;
        int ret = n;
        for(int i = 0;i<32;i++)
        {
            if(ret & 1 == 1) sum++;
            ret = ret >> 1;
        }
        return sum;
    }
};

第二题、461. 汉明距离

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

解析:

根据题目来看,可能最先想到的就是异或操作,相同为 0,不同为 1。异或操作后结果为 0101,然后我们只需要统计出来二进制结果中 1 的个数就可以计算出来汉明距离啦。

原码:

class Solution {
public:
    int hammingDistance(int x, int y) {
        int ret = x ^ y;
        int sum = 0;
        //计算1的个数
        for(int i = 0;i<32;i++)
        {
            if(ret & 1 == 1) sum++;
            ret = ret >> 1;
        } 
        return sum;
    }
};

第三题、136. 只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

解析:

这是一道很典型的运用^的题目,我们只需要理解异或运算符,这题就迎刃而解啦。

原码:

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

第四题、260. 只出现一次的数字 III

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

解析:

本题是上一题的升级版,

把nums中的元素全部异或起来的结果eor就是那两个只出现一次的数字的异或结果。而这两个数不相同,意味着eor至少有一位是1,我们可以用lowbit运算拿到最低位的1,然后遍历nums数组,将所有数nums[i]按照这一位是不是1分成两类,初始化num1=num2=0

  1. 如果当前位是1,就将nums[i]异或到num1​上。

  2. 如果当前位是0,就将nums[i]异或到num2​上。

这样一来,两个只出现一次的数就会被分别异或到num1​和num2​上,而其他数也会被分别异或到这两个数上。而由于其他数都出现了两次,所以最终它们就会被异或成0,num1​和num2​就是那两个只出现一次的数。

注意进行位运算的优先级,直接加上括号就好!!!

原码:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int a = 0, b = 0;//记录两个不同的数
        int ret = 0;
        for(int i = 0;i<nums.size();i++)
            ret ^= nums[i];
        //进行lowbit运算,两者不同,二进制肯定有1
        int tmp = ret & (-(long long)ret);//防止数据溢出
        for(int i = 0;i<nums.size();i++)
        {
            if((tmp & nums[i]) == 0)//注意优先级! 
                a ^= nums[i];
            else b ^= nums[i];
        }
        return {a,b};
    }
};

第五题、面试题 01.01. 判定字符是否唯一

解析:

本题第一思想直接用哈希表解决,但题目中用说尽量不用数据结构,我们可以尝试用位图解决!

用位图解决,需要熟练掌握位运算技巧,对巩固位运算有很大帮助!

原码:

class Solution {
public:
    bool isUnique(string astr) {
        int bitmap = 0;//用位图思想解决
        int tmp = 0;
        int n = astr.size();
        //利用鸽巢原理优化
        if(n > 26) return false;
        for(int i = 0;i<astr.size();i++)
        {
            tmp = astr[i] - 'a';
            //判断字符是否已经出现过
            if(((bitmap >> tmp) & 1) == 1) return false;
            //把当前字符加入位图中
            bitmap = bitmap | (1 << tmp);
        }
        return true;
    }
};

第六题、371. 两整数之和

给你两个整数 a 和 b ,不使用 运算符 + 和 - ,计算并返回两整数之和。

思路:

我们前面介绍了^运算的另一个功能是不进位相加,因此我们可以利用这个特性去解决这道题。

因为是不进位,所以我们要想办法去解决进位的问题,^运算是相同为0,相异为1,相异不可能进位,只有相同并且都是1的情况下,才会进位,因此我们直接&,查找出都是1,因为是进位,所以还要左移一位,(a & b)<< 1,然后分别重制a,b的值。

原码:

class Solution {
public:
    int getSum(int a, int b) {
        while(b)
        {
            int tmp = a;
            a = a ^ b;
            b = ((tmp&b) << 1);
        }
        return a;
    }
};

第七题、137. 只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

解析:

本题有点难度,两层嵌套循环,根据32位int,把每个数位相加再对3取的余数即可,将每个数想象成32位的二进制,对于每一位的二进制的1和0累加起来必然是3N或者3N+1, 为3N代表目标值在这一位没贡献,3N+1代表目标值在这一位有贡献(=1),然后将所有有贡献的位|起来就是结果。这样做的好处是如果题目改成K个一样,只需要把代码改成cnt%k,很通用~

原码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        int n = nums.size();
        for(int i = 0;i<32;i++)//依次去修改ans的每一位
        {
            int sum = 0;
            for(int j = 0;j<n;j++)
            {
                //计算nums中所有数的第i位的和
                sum += (nums[j] >> i) & 1;
            }
            //把第i位修改
            if(sum % 3)   
                ans ^= (1 << i); 
        }
        return ans;
    }
};

第八题、面试题 17.19. 消失的两个数字

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

解析:

本题是两道题的融合版。

  • 将所有的数异或在一起,记为tmp
  • 找到tmp中,比特位上为1的那一位
  • 根据不同的那一位,划分为两类异或

原码

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int a = 0,b = 0;
        int n = nums.size();
        int ret = 0;
        //先将所有数异或
        for(int i = 1;i<=n+2;i++)
            ret ^= i;
        for(int i = 0;i<n;i++)
            ret ^= nums[i];
        //lowbit运算找到最右边的1
        int tmp = ret & (-(long long)ret);//防止溢出
        for(int i = 0;i<n;i++)
        {
            if((nums[i] & tmp) == 0) a ^= nums[i];
            else b ^= nums[i];
        }
        for(int i = 1;i<=n+2;i++)
        {
            if((i & tmp) == 0) a ^= i;
            else b ^= i; 
        }
        return {a,b};
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud Aliba-Sentinel【中篇】-从入门到学废【5】

&#x1f3b5;歌词分享&#x1f3b5; 岁月在墙上剥落看见小时候。 ——《东风破》 目录 &#x1f953;1.流控规则 &#x1f32d;2. 熔断规则 &#x1f9c8;3.热点规则 &#x1f9c2;4.系统规则 1.流控规则 1.资源名&#xff1a;唯一名称&#xff0c;默认请求路径 2.针对来…

【开源】基于JAVA语言的教学资源共享平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 类图设计3.3 数据库设计3.3.1 课程档案表3.3.2 课程资源表3.3.3 课程作业表3.3.4 课程评价表 四、系统展…

[AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言4.5key价格泄漏ChatGPT4.0使用地址ChatGPT正确打开方式最新功能语音助手存档…

SpringBoot项目中简单使用虚拟机Redis

目录 步骤大致如下&#xff1a; 一.在pom文件中加入redis依赖 二.在虚拟机上打开我们下载好的Redis。开启服务器端并获取虚拟机ip地址 三.在项目配置。 四&#xff1a;使用redis 测试 redis是一个以键值对存储的NoSQL。被数百万开发人员用作缓存、矢量数据库、文档数据库、…

beego项目部署与热更新

1.开发自己的第一个项目 这里我引用的是在线聊天室&#xff0c;参考源码是https://github.com/beego/samples/tree/master/WebIM 在源码的基础上重新开发&#xff0c;整理项目发布到了liu289747235/WebIM 推荐下载源码&#xff1a;https://gitee.com/myselfyou/web-im 在线…

kafka参数配置参考和优化建议 —— 筑梦之路

对于Kafka的优化&#xff0c;可以从以下几个方面进行思考和优化&#xff1a; 硬件优化&#xff1a;使用高性能的硬件设备&#xff0c;包括高速磁盘、大内存和高性能网络设备&#xff0c;以提高Kafka集群的整体性能。 配置优化&#xff1a;调整Kafka的配置参数&#xff0c;包括…

go语言(七)----slice的声明方式

1、声明方式一 //声明一个slice1是一个切片&#xff0c;但是并没有给slice分配空间var slice1 []intslice1 make([]int,3)2、声明方式二 声明一个slice切片&#xff0c;同时给slice分配空间&#xff0c;3个空间&#xff0c;初始化值是0var slice1 []int make([]int,3)3、声…

Docker:6种网络配置详解浅介

在Docker中&#xff0c;网络配置是一个重要的主题&#xff0c;因为容器需要与其他容器或外部网络进行通信。Docker提供了多种网络模式和配置选项&#xff0c;以便在不同的场景下满足用户的需求。 本文介绍这些网络模式的区别以及配置&#xff0c;相信看完以后你能够掌握Docker网…

基于springboot+vue养老院管理系统

摘要 这是一个基于Spring Boot 和 Vue.js 的养老院管理系统的项目。该系统旨在提供一套全面的解决方案&#xff0c;以简化养老院的日常管理任务&#xff0c;包括居民信息管理、员工调度、医疗服务追踪、财务管理等。通过结合后端的Spring Boot框架和前端的Vue.js框架&#xff0…

【LeetCode】每日一题 2024_1_20 按分隔符拆分字符串(模拟/库函数)

文章目录 随便聊聊时间题目&#xff1a;按分隔符拆分字符串题目描述代码与解题思路 随便聊聊时间 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 时隔半个月&#xff0c;LeetCode 每日一题重新开张&#xff0c;寒假学习&#xff0c;正式开始 题目&#xff1…

算法笔记(动态规划入门题)

1.找零钱 int coinChange(int* coins, int coinsSize, int amount) {int dp[amount 1];memset(dp,-1,sizeof(dp));dp[0] 0;for (int i 1; i < amount; i)for (int j 0; j < coinsSize; j)if (coins[j] < i && dp[i - coins[j]] ! -1)if (dp[i] -1 || dp[…

calloc与realloc和malloc的区别以及new

目录 calloc、realloc 和 malloc 三个函数的区别在于 更详细的示例代码 交叉使用 内存泄漏 悬空指针 内存重叠 new 的语法 使用 new 运算符在堆上创建学生对象的示例 new和malloc都可以用于在堆上分配内存 calloc、realloc 和 malloc 是 C/C 中用于动态内存分配的函…

链表的相交

链表的相交 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/intersection-of-tw…

【NVIDIA】Jetson Orin Nano系列:安装 Qt6、firefox、jtop、flameshot

1、使用命令安装 sudo apt install qtcreator sudo apt install qt6-* sudo apt install libqt6* sudo apt install qml-qt6 sudo apt install qmlscene-qt6 sudo apt install assistant-qt6 sudo apt install designer-qt62、启动 qtcreator 3、常用工具安装 sudo apt in…

ros2学习笔记-CLI工具,记录命令对应操作。

目录 环境变量turtlesim和rqt以初始状态打开rqt node启动节点查看节点列表查看节点更多信息命令行参数 --ros-args topic话题列表话题类型话题列表&#xff0c;附加话题类型根据类型查找话题名查看话题发布的数据查看话题的详细信息查看类型的详细信息给话题发布消息&#xff0…

线程状态转换

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程⛺️稳中求进&#xff0c;晒太阳 程状态转换 假设有线程Thread t 情况1 new-->RUNNABLE 当调用t.start()方法时&#xff0c;由new ->RUNNABLE 情况2 RUNNABLE WAITING t…

无/自监督去噪(1)——一个变迁:N2N→N2V→HQ-SSL

目录 1. 前沿2. N2N3. N2V——盲点网络&#xff08;BSNs&#xff0c;Blind Spot Networks&#xff09;开创者3.1. N2V实际是如何训练的&#xff1f; 4. HQ-SSL——认为N2V效率不够高4.1. HQ-SSL的理论架构4.1.1. 对卷积的改进4.1.2. 对下采样的改进4.1.3. 比N2V好在哪&#xff…

c++基础2

一、c的引用 引用和指针的的区别&#xff1f; 引用是一种更安全的指针&#xff1a; 1. 引用必须初始化&#xff0c;指针可以不用初始化 int a 10; int *p; // 指针可能是野指针 int &b a;//引用赋值"&#xff0c;通常指的是直接修改引用所引用的对象的值&#xff0…

Three.JS教程1 环境搭建、场景与相机

Three.JS教程1 环境搭建、场景与相机 一、Three.JS简介二、环境搭建1. 开发准备2. 安装 three.js3. 新建文件index.htmlmain.js 4. 关于附加组件5. 启动 三、创建场景1. 场景的概念2. 相机的概念3. 相机的几个相关概念&#xff08;1&#xff09;视点&#xff08;Position&#…

信息登记小程序怎么做_重塑用户互动,开启全新营销篇章

信息登记小程序&#xff1a;重塑用户互动&#xff0c;开启全新营销篇章 在数字化浪潮中&#xff0c;小程序以其便捷、高效的特点&#xff0c;逐渐成为企业与用户之间沟通的桥梁。其中&#xff0c;信息登记小程序更是凭借其独特的定位&#xff0c;在众多小程序中脱颖而出。本文…