算法-位运算基础

文章目录

    • 1. 前置知识
    • 2. 交换两个数
    • 3. 比较两个数的大小
    • 3. leetcode268 寻找缺失的数字
    • 4. leetcode136 只出现一次的数字
    • 5. leetcode260 只出现一次的数字|||
    • 6. leetcode137 只出现一次的数字||
    • 7. 2/3的幂
    • 8. 大于等于该数字的最小2的幂
    • 9. leetcode201 数字范围按位与
    • 10. 位运算中分治法举例

1. 前置知识

本节最重要的一个算法 Brain Kernighan算法
大致内容如下 :
如何提取出来一个数的最右侧的1
n = n & (~n + 1)
因为 (~n + 1) == -n
所以该算法也可以写成
n = n & (-n)

2. 交换两个数

因为我们的异或运算遵循的交换律和结合律, 所以我们可以写出下面的这一段代码

public void swap(int[] arr, int i, int j) {//注意这里的 i != jarr[i] = arr[i] ^ arr[j];arr[j] = arr[i] ^ arr[j];arr[i] = arr[i] ^ arr[j];}

请注意这里面的 i != j 因为如果相等的话, 会直接把这一块区域置为0

3. 比较两个数的大小

尝试不用任何的比较操作就比较出来两个数的大小关系, 我们写出来的代码如下

/*** 不包含比较相关的逻辑运算符来得到两个数当中的最大值*///flip意为反转 --> 此时传入的n保证为 1 / 0 --> 如果是1, 就转化为0 , 如果是0, 就转化为1private int flip(int n) {return n ^ 1;}//获取一下某个数字的符号,如果是非负的数字就返回一个1, 如果是负数就返回0private int sign(int n) {return flip(n >>> 31);}//下面我们给出两种判断的方法 --> 第一种有可能会溢出, 第二种不会溢出//核心的逻辑就是设置 returnA , returnB 来规定什么时候返回a, 什么时候返回b(两个return一定是互斥的才可以)public int getMax1(int a, int b) {//c可能会溢出int c = a - b;int returnA = sign(c);int returnB = flip(returnA);return a * returnA + b * returnB;}

解释一下我们的代码 :
sign方法是为了获取到某一个数的符号, 如果是非负数就返回1, 如果是负数就返回0, flip方法是为了把翻转数字,保证传入的数字一定是0或1, 其实这段代码的本质逻辑就是定义了两个returnA和returnB变量, 这两个变量一定是互斥的(一个1,一个0), 在必要的条件下就返回值
举例:
比如 a == 5, b == 3, c == 2, returnA为获取一下c的符号是非负也就是1, 那么returnB就是相反的0, 所以最后return的结果就是returnA * a == a
思考 : 上述代码是不是有问题呢?
其实, 当我们的c是有可能发生溢出的, 比如a = Integer.MAX_VALUE;
b = Integer.MIN_VALUE, 显然我们的c是溢出的, 所以我们的代码是有问题的, 我们的改动如下(flip和sign方法是不变的)

//下面的这个方法是不会溢出的public int getMax2(int a, int b) {//c依然可能会溢出int c = a - b;//下面判断一下a b c的符号int signA = sign(a);int signB = sign(b);int signC = sign(c);//判断a b 符号是不是相同的int diffAB = signA ^ signB; //不同返回1int sameAB = flip(diffAB); //相同返回1//什么时候进行a的返回 //--> 1. ab不同号且a为非负//--> 2. ab同号(此时不可能会溢出)且signC == 1int returnA = diffAB * signA + sameAB * signC;int returnB = flip(returnA);return a * returnA + b * returnB;}

上述的代码逻辑和之前的那个其实是一致的
就是加入了一些是否越界的判断(到底什么时候returnA)

3. leetcode268 寻找缺失的数字

在这里插入图片描述

前置知识
假设一堆数字异或的结果我们记为异或和 : ans
这一堆数字的部分数字的结果我们记作 : eor1
另一部分记作 : eor2
显然有 eor1 ^ eor2 = ans
两边同时 ^ eor1
可以得到 eor2 = ans ^ eor1
有了上面的结论的铺垫, 写这个题应该是十分的容易, 代码实现如下

class Solution {public int missingNumber(int[] nums) {//说白了这个题就是异或运算的性质int eor = 0;for(int elem : nums){eor ^= elem;}int eorN = 0;for(int i = 0; i <= nums.length; ++i){eorN ^= i;}return eorN ^eor;}
}

4. leetcode136 只出现一次的数字

在这里插入图片描述

这个问题可以抽象为下面的这个问题
给你一个数组, 其中一种数字出现了奇数次, 另外的数字都出现了偶数次
比较简单好想的思路是创建一个Set集合对元素进行去重操作(略)
这里我们用异或运算来写
思考 : 由于异或运算满足交换律, 不管多少个偶数个相同数字异或的结果一定是0
所以只要异或一轮就是那个结果, 代码实现如下

class Solution {public int singleNumber(int[] nums) {int eor = 0;for(int element : nums){eor = eor ^ element;}return eor;}
}

5. leetcode260 只出现一次的数字|||

在这里插入图片描述

该问题与上面的问题类似, 我们把该问题抽象出来就是, 给了一组数字, 其中有两个元素出现了奇数次, 其他的所有元素出现了偶数次, 求出来两个元素
思路分析 :
先把所有的元素异或起来得到一个异或和 eor , 该异或和的结果就是那两个数字(我们简介为m,n)
也就是 m ^ n == eor , 然后通过Brain Kernighan算法, 得到了最右侧的1, 因为我们的异或运算也可以等同于无进位相加, 所以这个1必定来源于m和n的其中一个, 所以我们定义一个eorN,让这个eorN只异或该位为1的数字, 得到的eorN就是m/n的其中一个,问题得解

class Solution {public int[] singleNumber(int[] nums) {int eor = 0;for(int elem : nums){eor ^= elem;}int n = eor & (~eor + 1);int eorN = 0;for(int elem : nums){if((elem & n) == 0){eorN ^= elem;}}return new int[]{eorN, eor ^ eorN};}
}

6. leetcode137 只出现一次的数字||

在这里插入图片描述

把该问题抽象出来, 就是一组数字, 其中一个数字出现次数小于m次, 其他的所有数字都出现了m次, 求出来这个数字是多少
思路分析 :
我们通过分位操作, 同意每一位上出现的1的个数, 遍历这个数组, 如果是hash[i] % m != 0, 就证明该数字这一位是1, 问题得解
代码实现如下

class Solution {public int singleNumber(int[] nums) {//首先进行的是统计每一个数位上的1的个数int[] hash = new int[32];for(int elem : nums){for(int i = 0; i < 32; ++i){hash[i] = hash[i] + ((elem >>> i) & 1);}}//统计1的分位个数完毕, 开始还原数字int ans = 0;for(int i = 0; i < 32; ++i){if(hash[i] % 3 != 0){ans = ans | (1 << i);}}return ans;}
}

7. 2/3的幂

给一个数判断是不是2的幂, 这个没什么可说的, 直接用Brain Kernighan算法

class Solution {/*** 判断一个数字是不是2的幂* @param n* @return*/public boolean isPowerOfTwo(int n){return n > 0 && (n & (~n + 1)) == n;}/*** 判断一个数字是不是3的幂(直接找到int范围内3的最大的幂是多少* @param n* @return*/public boolean isPowerOfThree(int n){return n > 0 && 1162261467 % n == 0;}}

8. 大于等于该数字的最小2的幂

已知n是非负数, 请返回大于等于n的最小的2的幂
思路分析 :
假如数字的二进制序列是 : …0010100110 , 那么此时大于等于该数的最小的2的幂就是最左侧1的一个位置
假如数字的二进制序列是 : …0001000000 , 那么此时大于等于该数的最小的2的幂就是该数本身
假设有一种方案可以把 最左侧的1后面的所有二进制位都刷成1 , 那么+1以后的结果就是答案
为什么要先进行 n-- , 是为了满足n正好是2的幂的情况
下面的几行代码的作用就是将最左侧的1的右面的二进制位全部刷成1
代码实现如下

class Solution{public int nearTwoPower(int n){if(n <= 1){return 1;}n--;n = n | (n >>> 1);n = n | (n >>> 2);n = n | (n >>> 4);n = n | (n >>> 8);n = n | (n >>> 16);return n + 1;}
}

9. leetcode201 数字范围按位与

在这里插入图片描述

暴力算法肯定是不可取的, 所以我们要采取更加好的算法
指定区间按位与(肯定是不能暴力解法)
思考 : 假设我们的left == right, 那么我们最终的与的结果就是left / right
如果 left != right, 那么此时我让right减小一点, 也就是(right - 1) & left
那么从 m ~ right的所有数字与起来的结果是不变的都是m, 因为前缀不变, 后缀全是0
循环下去, 直到 left >= right (中间的过程用的是Brain Kernighan算法)
代码实现如下

class Solution{public int rangeBitswiseAnd(int left,int right){while (left < right) {right = right - (right & (~right + 1));}return right;}
}

10. 位运算中分治法举例

我们给出来两道题, 第一道题就是逆序二进制数位, 第二道题就是汉明距离(统计二进制中的1的个数) , leetcode的题号 190 . 461
这个自己慢慢悟吧, 下面给出来代码实现(让我自己懂的)

class Solution{/*** 翻转二进制的状态* 比如一个数的二进制位是 : 0001101101010010 --reverse--> 010010101011000* 分析 : 正常的解法就是定义一个ans, 看到哪一位上有i你就或上去一个1就行了, 这里我们不在多说, 有点简单*       我们重点说一下位运算分治的思路(从两个一组翻转 --> 四个一组 --> 八个一组.....)*       假如有一个序列  a b c d e f g h*       我们翻转的过程: 1. b a d c f e h g (1v1翻转)*                    2. d c b a h g f e (2v2翻转)*                    3. h g f e d c b a (4v4翻转)*       下面推广到代码上 :*                   1. a b c d e f g h & 0 1 0 1 0 1 0 1  ==> 0 b 0 d 0 f 0 h (1)*                      a b c d e f g h & 1 0 1 0 1 0 1 0 ==>  a 0 c 0 e 0 g 0 (2)*                      (1) << 1 | (2) >>> 1  ==> b a d c f e h g*       下面的过程以此类推* @param n* @return*/public int reverseBits(int n){n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >>> 1);n = ((n & 0x33333333) << 2) | ((n & 0xcccccccc) >>> 2);n = ((n & 0x0f0f0f0f) << 4) | ((n & 0xf0f0f0f0) >>> 4);n = ((n & 0x00ff00ff) << 8) | ((n & 0xff00ff00) >>> 8);n = ((n & 0x0000ffff) << 16) | ((n & 0xffff0000) >>> 16);return n;}/*** 计算一个数字的二进制位中有几个1* 还是用的位运算分治的方法*   思路分析 : 假如数字的二进制位是  1 0 1 1 0 1 0 1** @param n* @return*/public int cntOnes(int n){n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);return n;}
}

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

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

相关文章

Docker部署Dillinger个人文本编辑器

Docker部署Dillinger个人文本编辑器 一、Dillinger介绍1.1 Dillinger简介1.2 Dillinger使用场景 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、拉取Dillinger镜像五、部署Dill…

汇聚荣拼多多运营好吗?

在当前电商行业竞争激烈的背景下&#xff0c;拼多多凭借其独特的商业模式和市场定位迅速崛起&#xff0c;成为众多商家和品牌争相入驻的平台。对于“汇聚荣拼多多运营好吗?”这一问题&#xff0c;答案是肯定的。拼多多不仅提供了一个广阔的市场空间&#xff0c;还通过其创新的…

HTML5+JavaScript单词游戏

HTML5 JavaScript单词游戏 数据字典格式&#xff1a;每行一个 单词 &#xff0c;单词和解释用空格分隔&#xff0c;如 a art.一(个)&#xff1b;每一(个) ability n.能力&#xff1b;能耐&#xff0c;本领 able a.有能力的&#xff1b;出色的 baby n.婴儿&#xff1b;孩子…

数据库对比脚本,java如何对比两个数据库的表字段的不同

因为有时候开发环境和 测试环境&#xff0c;有时候会有不同的数据库表&#xff0c;比如有些加字段了&#xff0c;所以这个脚本就实现了对比两个数据库连接的数据库到底哪里不一样&#xff0c;输出到控制台 package com.junfun.pms;import lombok.extern.slf4j.Slf4j;import ja…

25届最近5年北京工业大学自动化考研院校分析

北京工业大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、初试大纲复试大纲 七、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试科目介绍 2、指定教…

C++进修——C++核心编程

内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制编码&#xff0c;由操作系统进行管理全局区&#xff1a;存放全局变量和静态变量以及常量栈区&#xff1a;由编译器自动分配释放&#xff0c;存放函数的参数值&#xff…

grpc学习golang版( 三、proto文件数据类型 )

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、proto语法二、proto中的标量类型三、proto中的数组类型四、p…

资料导览(持续更新)

经典推荐 多模态大模型&#xff1a;基础架构 图解DSPy&#xff1a;Prompt的时代终结者&#xff1f;&#xff01; vLLM, LMDeploy, MLC-LLM, TensorRT-LLM, and TGI的性能小实验 优雅谈大模型13&#xff1a;一文读懂LoRA/DoRA/MoRA 新鲜速递&#xff1a;图解新颖LLM的CoPE位…

C#测试调用DotnetSpider爬取网页内容

微信公众号“DotNet”的文章《.NET快速实现网页数据抓取》介绍了调用开源网页爬取模块DotnetSpider爬取cnblog网站文章的基本方式。之前学习过使用HtmlAgilityPack抓取并分析网页内容&#xff0c;DotnetSpider也依赖HtmlAgilityPack模块&#xff0c;不过前者属于轻量、高效的爬…

C++ 运算符的优先级和结合性表

优先级和结合性表 优先级运算符描述结合性1::作用域解析运算符左到右2() [] . -> --后缀运算符左到右3 -- - ! ~ * & sizeof new delete typeid一元运算符右到左4* / %乘除取模左到右5 -加法和减法左到右6<< >>左移和右移左到右7< < > >关系…

大数据开发如何管理项目

在面试的时候总是 会问起项目&#xff0c;那在大数据开发的实际工作中&#xff0c;如何做好一个项目呢&#xff1f; 目录 1. 需求分析与项目规划1.1 需求收集与梳理1.2 可行性分析1.3 项目章程与计划 2. 数据准备与处理2.1 数据源接入2.2 数据仓库建设2.3 数据质量管理 3. 系统…

【微服务】Alibaba Cloud Linux环境下Docker以及MySQL安装

部署Docker 1.安装dnf dnf是新一代的rpm软件包管理器 yum -y install dnf2.安装社区版Docker&#xff08;docker-ce&#xff09; 添加docker-ce的dnf源 dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Alibaba Cloud…

MySQL 12种锁:真实业务与流程图解析

文章目录 1. 表级锁&#xff08;Table Lock&#xff09;场景1&#xff1a;全表扫描统计 2. 行级锁&#xff08;Row Lock&#xff09;场景2&#xff1a;修改特定用户信息 3. 全局锁&#xff08;Global Lock&#xff09;场景3&#xff1a;数据备份 4. 意向锁&#xff08;Intent L…

高性能并行计算华为云实验三:蒙特卡罗算法实验

目录 一、实验目的 二、实验说明 三、实验过程 3.1 创建蒙特卡罗算法源码 3.2 Makefile的创建与编译 3.3 主机文件配置与运行监测​​​​​​​ 四、实验结果与分析 4.1 原教程对应的实验结果 4.2 改进后的实验结果 五、实验思考与总结 5.1 实验思考 5.2 实验总结…

firewalld(2)安装、配置文件、规则查询

安装firewalld 我使用的操作系统是debian 12,并没有安装firewalld。 通过apt install firewalld安装firewalld firewalld 本身是一个服务(firewalld.service),可以通过 systemctl 进行启动、停止和重启,而iptables 本身并不是一个服务,而是一个用户空间工具,被用来配置底…

论文浅尝 | 通过基于动态文档知识图谱增强的大语言模型故事理解

笔记整理&#xff1a;许方舟&#xff0c;天津大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/21286 1. 动机 基于大型 Transformer 的语言模型在需要叙事理解的各种任务上取得了令人难以置信的成功&#xff0c;包括…

python中容易错误的知识点,仅仅针对于自己

dtype 参数在创建 NumPy ndarray 对象时用于指定数组元素的数据类型。 在 NumPy 中&#xff0c;直接使用 Python 列表即可创建 ndarray 对象&#xff0c;无需使用 list() 函数。 dtype 属性正确地表示了数组中元素的数据类型&#xff0c;所以描述没有错误。实际上&#xff0c…

【Hadoop学习笔记】认识Hadoop

认识Hadoop 从网上找的课程做的笔记&#xff0c;有些图是自己理解画的&#xff0c;可能不正确&#xff0c;可以作为参考&#xff0c;有疑问的地方请直接指出&#xff0c;共同交流。 Hadoop是由Apache基金会开发的一个分布式系统基础架构&#xff0c;主要解决海量数据的存储和海…

安装mmdetection

python版本&#xff1a;3.7 torch1.10.0,torchvision0.11.0 torchaudio0.10.0 cudatoolkit11.3.1 mmdetection版本:2.11.0 mmcv-full: 根据自己的cuda版本和torch版本修改下面的网址&#xff0c;找到对应的下载并安装 https://download.openmmlab.com/mmcv/dist/cu113/torch1.…

Django ModelForm:循环展示所有字段

在Django开发中,使用ModelForm可以大大简化表单的创建和处理过程。本文将介绍如何使用ModelForm,并展示一种简洁的方法来循环显示表单中的所有字段。 1. 模型定义 首先,让我们看一下我们的模型定义: # models.py from django.db import modelsclass Classxxxx(models.Mo…