Datawhale Leecode基础算法篇 task05:位运算

官方学习文档:datawhalechina

往期task01:枚举算法链接:Datawhale Leecode基础算法篇 task01:枚举算法

往期task02:递归算法and分治算法:Datawhale Leecode基础算法篇 task02:递归算法and分治算法

往期task03:回溯算法:Datawhale Leecode基础算法篇 task03:回溯算法

往期task04:贪心算法:Datawhale Leecode基础算法篇 task04:贪心算法

位运算

位运算简介

位运算(Bit Operation):在计算机内部,数是以「二进制(Binary)」的形式来进行存储。位运算就是直接对数的二进制进行计算操作,在程序中使用位运算进行操作,会大大提高程序的性能。

二进制数(Binary):由 0 和 1 两个数码来表示的数。二进制数中每一个 0 或每一个 1 都称为一个「位(Bit)」。

在二进制数中,我们只有 0 和 1 两个数码,它的进位规则是「逢二进一」。 

二进制转十进制数:

十进制转二进制数:

十进制数转二进制数的方法是:除二取余,逆序排列法

具体可以参考这个视频:傻瓜式十进制转二进制,二进制转十进制

位运算基础操作

在二进制的基础上,我们可以对二进制数进行相应的位运算。基本的位运算共有 6 种,分别是:「按位与运算」、「按位或运算」、「按位异或运算」、「取反运算」、「左移运算」、「右移运算」。

这里的「按位与运算」、「按位或运算」、「按位异或运算」、「左移运算」、「右移运算」是双目运算。

  • 「按位与运算」、「按位或运算」、「按位异或运算」是将两个整数作为二进制数,对二进制数表示中的每一位(即二进位)逐一进行相应运算,即双目运算。
  • 「左移运算」、「右移运算」是将左侧整数作为二进制数,将右侧整数作为移动位数,然后对左侧二进制数的全部位进行移位运算,每次移动一位,总共移动右侧整数次位,也是双目运算。

而「取反运算」是单目运算,是对一个整数的二进制数进行的位运算。

我们先来看下这 6 种位运算的规则,再来进行详细讲解。

运算符描述规则
|按位或运算符只要对应的两个二进位有一个为 1 时,结果位就为 1。
&按位与运算符只有对应的两个二进位都为 1 时,结果位才为 1。
<<左移运算符将二进制数的各个二进位全部左移若干位。<< 右侧数字指定了移动位数,高位丢弃,低位补 0。
>>右移运算符对二进制数的各个二进位全部右移若干位。>> 右侧数字指定了移动位数,低位丢弃,高位补 0。
^按位异或运算符对应的两个二进位相异时,结果位为 1,二进位相同时则结果位为 0。
~取反运算符对二进制数的每个二进位取反,使数字 1 变为 0,0 变为 1。

位运算的应用

 判断整数奇偶

一个整数,只要是偶数,其对应二进制数的末尾一定为 0;只要是奇数,其对应二进制数的末尾一定为 1。所以,我们通过与 1 进行按位与运算,即可判断某个数是奇数还是偶数。

  1. (x & 1) == 0 为偶数。
  2. (x & 1) == 1 为奇数。
二进制数选取指定位

如果我们想要从一个二进制数 X 中取出某几位,使取出位置上的二进位保留原值,其余位置为 0

则可以使用另一个二进制数 Y,使该二进制数上对应取出位置为 1,其余位置为 0。然后令两个数进行按位与运算(X & Y),即可得到想要的数。

我们要取二进制数 X=01101010(2) 的末尾 4 位,则只将 X=01101010(2) 与 Y=00001111(2) (末尾 4 位为 1,其余位为 0) 进行按位与运算,即 01101010 & 00001111 == 00001010。其结果 00001010 就是我们想要的数(即二进制数 01101010(2) 的末尾 4 位)。

 将指定位设置为 1

如果我们想要把一个二进制数 X 中的某几位设置为 1,其余位置保留原值,则可以使用另一个二进制数 Y,使得该二进制上对应选取位置为 1,其余位置为 0。然后令两个数进行按位或运算(X | Y),即可得到想要的数。

举个例子,比如我们想要将二进制数 X=01101010(2) 的末尾 4 位设置为 1,其余位置保留原值,则只需将 X=01101010(2) 与 Y=00001111(2)(末尾 4 位为 1,其余位为 0)进行按位或运算,即 01101010 | 00001111 = 01101111。其结果 01101111 就是我们想要的数(即将二进制数 01101010(2) 的末尾 4 位设置为 1,其余位置保留原值)。

反转指定位

如果我们想要把一个二进制数 X 的某几位进行反转,则可以使用另一个二进制数 Y,使得该二进制上对应选取位置为 1,其余位置为 0。然后令两个数进行按位异或运算(X ^ Y),即可得到想要的数。

1对0得1,0对0得0,保持原值

1对1得0,0对1得1,数值反转

举个例子,比如想要将二进制数 X=01101010(2) 的末尾 4 位进行反转,则只需将 X=01101010(2) 与 Y=00001111(2)(末尾 4 位为 1,其余位为 0)进行按位异或运算,即 01101010 ^ 00001111 = 01100101。其结果 01100101 就是我们想要的数(即将二进制数 X=01101010(2) 的末尾 4 位进行反转)。

交换两个数 

通过按位异或运算可以实现交换两个数的目的(只能用于交换两个整数)。 

a, b = 10, 20
a ^= b
b ^= a
a ^= b
print(a, b)
将二进制最右侧为 1 的二进位改为 0(即从右向左数第一个1改为0)

如果我们想要将一个二进制数 X 最右侧为 1 的二进制位改为 0,则只需通过 X & (X - 1) 的操作即可完成。

比如 X=01101100(2),(X-1)=01101011,则 X & (X - 1) == 01101100 & 01101011 == 01101000,结果为 01101000(2)(即将 X 最右侧为 1 的二进制为改为 0)。

计算二进制中二进位为 1 的个数

如上通过 X & (X - 1) 我们可以将二进制 X 最右侧为 1 的二进制位改为 0,那么如果我们不断通过 X & (X - 1) 操作,最终将二进制 X 变为 0,并统计执行次数,则可以得到二进制中二进位为 1 的个数。

具体代码如下:

class Solution:def hammingWeight(self, n: int) -> int:cnt = 0while n:n = n & (n - 1)cnt += 1return cnt
判断某数是否为 2 的幂次方

通过判断 X & (X - 1) == 0 是否成立,即可判断 X 是否为 2 的幂次方。

这是因为:

  1. 凡是 2 的幂次方,其二进制数的某一高位为 1,并且仅此高位为 1,其余位都为 0。比如:$4_{(10)} = 00000100_{(2)}$$8_{(10)} = 00001000_{(2)}$
  2. 不是 2 的幂次方,其二进制数存在多个值为 1 的位。比如:$5_{10} = 00000101_{(2)}$$6_{10} = 00000110_{(2)}$

我们使用 X & (X - 1) 操作,将原数对应二进制数最右侧为 1 的二进位改为 0 之后,得到新值,若原数是 2 的幂次方,则通过 X & (X - 1) 操作之后,新值所有位都为 0,值为 0;反之值不为0则不是 2 的幂次方。

位运算的常用操作总结
功 能位运算示例
从右边开始,把最后一个 1 改写成 0x & (x - 1)100101000 -> 100100000
去掉右边起第一个 1 的左边x & (x ^ (x - 1)) 或 x & (-x)100101000 -> 1000
去掉最后一位x >> 1101101 -> 10110
取右数第 k 位x >> (k - 1) & 11101101 -> 1, k = 4
取末尾 3 位x & 71101101 -> 101
取末尾 k 位x & 151101101 -> 1101, k = 4
只保留右边连续的 1(x ^ (x + 1)) >> 1100101111 -> 1111
右数第 k 位取反x ^ (1 << (k - 1))101001 -> 101101, k = 3
在最后加一个 0x << 1101101 -> 1011010
在最后加一个 1(x << 1) + 1101101 -> 1011011
把右数第 k 位变成 0x & ~(1 << (k - 1))101101 -> 101001, k = 3
把右数第 k 位变成 1x | (1 << (k - 1))101001 -> 101101, k = 3
把右边起第一个 0 变成 1x | (x + 1)100101111 -> 100111111
把右边连续的 0 变成 1x | (x - 1)11011000 -> 11011111
把右边连续的 1 变成 0x & (x + 1)100101111 -> 100100000
把最后一位变成 0x | 1 - 1101101 -> 101100
把最后一位变成 1x | 1101100 -> 101101
把末尾 k 位变成 1x | (1 << k - 1)101001 -> 101111, k = 4
最后一位取反x ^ 1101101 -> 101100
末尾 k 位取反x ^ (1 << k - 1)101001 -> 100110, k = 4
 二进制枚举子集 

除了上面的这些常见操作,我们经常常使用二进制数第 1∼n 位上 0 或 1 的状态来表示一个由 1∼n 组成的集合。也就是说通过二进制来枚举子集。

枚举子集的方法有很多,这里介绍一种简单有效的枚举方法:「二进制枚举子集算法」。

对于一个元素个数为 n 的集合 S 来说,每一个位置上的元素都有选取和未选取两种状态。我们可以用数字 1 来表示选取该元素,用数字 0 来表示不选取该元素。

对应代码:

class Solution:def subsets(self, S):                   # 返回集合 S 的所有子集n = len(S)                          # n 为集合 S 的元素个数sub_sets = []                       # sub_sets 用于保存所有子集for i in range(1 << n):             # 枚举 0 ~ 2^n - 1sub_set = []                    # sub_set 用于保存当前子集for j in range(n):              # 枚举第 i 位元素if i >> j & 1:              # 如果第 i 为元素对应二进位删改为 1,则表示选取该元素sub_set.append(S[j])    # 将选取的元素加入到子集 sub_set 中sub_sets.append(sub_set)        # 将子集 sub_set 加入到所有子集数组 sub_sets 中return sub_sets                     # 返回所有子集

 for i in range(1 << n)::这里使用了位移运算符<<,1 << n相当于2的n次幂,即从0开始枚举到2^n - 1。因为对于一个长度为n的集合,其子集的数量为2^n,所以我们可以通过遍历0到2^n - 1之间的整数来得到所有子集。

 if i >> j & 1::检查当前枚举的整数i的第j位是否为1。i >> j表示将i向右移动j位,& 1则是与1做位与操作,如果结果为1,则表示原数i的第j位为1,也就是说我们选择了集合S的第j个元素。

 sub_set.append(S[j]):如果第j位为1,那么将集合S的第j个元素添加到当前子集sub_set中。


那我们本期的学习在这里就短暂结束了,天涯何处不相逢,我们下次再见!

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

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

相关文章

STM32编码器接口笔记

1. 引言 在现代控制系统中&#xff0c;编码器扮演着非常重要的角色。它就像一个精密的测量工具&#xff0c;可以告诉我们机械部件的位置和运动状态。在STM32微控制器中&#xff0c;编码器接口可以轻松地与各种编码器连接&#xff0c;实现精确的控制。我将在这里探讨STM32编码器…

【FaceFusion3.0.0】全新升级,重磅发布!

FaceFusion 3.0.0 版本引入了许多新特性和改进&#xff0c;其中包括&#xff1a; 重新设计架构&#xff0c;使所有操作都作为“任务”进行处理。在面部交换功能中引入像素增强(pixel boost)。向面部检测器添加多角度处理功能。引入年龄修正处理器(age modifier processor)。引…

Kafak入门技术详解

抱歉&#xff0c;没有太多的时间进行详细校对 目录 一、Kafka简介 1.消息队列 1.1为什么需要消息队列 1.2消息队列 1.3消息队列的分类 1.4P2P和发布订阅MQ的比较 1.5消息系统的使用场景 1.6常见的消息系统 2.Kafka简介 2.1简介 2.2设计目标 2.3 kafka核心的概念 二…

Grafana指标汉化

1、Grafana解压 目录 conf 2、找到&#xff1a;defaults.ini 3、打开defaults.ini &#xff0c;搜索&#xff1a;en-US 4.重新运行 &#xff1a;grafana-server.exe

js中的深拷贝与浅拷贝 手写深拷贝代码

1 什么是深拷贝和浅拷贝&#xff1f; 深拷贝和浅拷贝都是复制对象时常用的两种方式&#xff0c;区别在于对于嵌套对象的处理&#xff0c;浅拷贝只复制属性的第一层属性&#xff0c;双方修改嵌套对象将会互相影响。深拷贝会递归复制每一层的属性&#xff0c;修改任意一方互不影响…

谷歌地图 | 3D 地图新功能:开发更简单,体验更丰富

今年早些时候在 Google I/O 大会上推出了地图 JavaScript API 中的逼真 3D 地图。从那时起&#xff0c;谷歌地图一直受到大家对 3D 地图的热烈反响&#xff0c;并从中汲取了大量灵感。9月25日&#xff0c;谷歌地图宣布实验性 3D 地图迎来了重大更新&#xff0c;这将使开发者更轻…

深度学习模型可视化工具 Netron 使用教程

Netron 介绍 Netron 是一个用于可视化机器学习模型、深度学习模型、神经网络、图模型&#xff08;例如用于计算机视觉的 ONNX、Caffe、TensorFlow Lite、TensorFlow.js、Keras、Darknet、TVM、PyTorch、TorchScript、Core ML、ML.NET、NNEF、PaddlePaddle、OpenVINO、Arm NN等…

2024年9月25日--- Spring-IOC 1

一 Spring的概要 1.1 简介 Spring&#xff0c;春天的意思&#xff0c;意指给软件行业带来春天。2002年&#xff0c;Rod Jahnson首次推出了Spring框架雏形interface21框架。2004年3月24日&#xff0c;Spring框架以interface21框架为基础&#xff0c;经过重新设计&#xff0c;发…

广州数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂

在广州&#xff0c;特别是在工业互联网领域&#xff0c;数字孪生技术正逐步成为赋能新型工业化智能制造工厂的重要驱动力。数字孪生工业互联网技术的引入&#xff0c;不仅为传统制造业带来前所未有的变革&#xff0c;更为广州的工业发展注入新的活力与可能。 在智能制造工厂的…

Linux 基础IO(个人笔记)

Linux基础 IO 1.C文件IO操作1.1 hello.c写文件1.2 hello.c读文件1.3 stdin&stdout&stderr 2.系统文件I/O2.1 hello.c写文件2.2 hello.c读文件2.3 open函数介绍2.4 文件描述符 fd2.4.1 文件描述符的分配规则2.4.2 重定向2.4.3 dup2系统调用2.4.4 C文件结构体FILE2.4.5 C…

了解输出电源优先级

主要又SUB&#xff0c;SBU以及USB三种模式。 调试10kW逆变器存在的输出电源优先级的问题&#xff0c;当优先级为SUB时&#xff0c;利用电压源模拟电池&#xff0c;当电池电压超过58.4V&#xff0c;即过压&#xff0c;在接入市电&#xff0c;市电继电器仍然闭合&#xff0c;仍然…

使用kubectl快速查看各个节点的CPU和内存占用量

本文章视频教程地址&#xff1a;https://www.bilibili.com/video/BV1TdxkedE1K 前言 笔者之前写过一篇文章关于在Kubernetes上安装 Prometheus 和 Grafana 监控去查看Kubernetes各个节点的资源占用率&#xff0c;文章地址&#xff1a;https://blog.csdn.net/m0_51510236/arti…

大模型(LLM) 是仅仅比 模型(Model) 更大吗?

我们日常经常提到模型 model&#xff0c;大模型LLM&#xff0c;大家都知道这两者之间的区别和联系吗&#xff1f; 只是如下图这样&#xff0c;大小的区别吗&#xff1f;下面我们对模型model和大模型LLM进行解释和描述 什么是模型&#xff1f; 模型是机器学习中一个核心概念&a…

matlab2019b-2024b knnclassify无法识别的问题(亲测,已解决)

matlab2019a-2024b 已经移除了knnclassify分类&#xff0c;修改了名称和功能&#xff0c;如果你还想使用它&#xff0c;就必须在2018版本以前的旧版本中找相关的工具箱&#xff08;这是免费的哦&#xff0c;如果官网下载 需要付费&#xff09;。 这里本人从2014a中分离出的工具…

JS设计模式之观察者模式:观察者与可观察对象的巧妙互动

一. 前言 在前端开发中&#xff0c;我们经常会遇到需要对用户的操作进行响应的场景&#xff0c;例如页面上的按钮点击、输入框内容变化等。为了实现这种响应式的设计&#xff0c;我们可以使用观察者模式来解耦各个组件之间的依赖关系。 本文将详细介绍观察者模式的原理和实现…

使用【apifox】进行压测-保姆级教程【无需脚本】

1.根据接口文档进行测试&#xff0c;写一个接口&#xff0c;能够调通即可 2.选择“从接口导入”&#xff0c;选择刚刚测试的接口 3.选择一个环境&#xff0c;我这里用的云服务器http://x.xx.xxx.xx &#xff08;端口号写不写都行&#xff0c;我是加上了&#xff09; 4.选性…

element-ui 通过按钮式触发日期选择器

element ui 写在前面1. 自定义的日期时间组件CustomDatePicker.vue2. 页面效果总结写在最后 写在前面 需求&#xff1a;elementui中日期时间选择器&#xff0c;目前只能通过点击input输入框触发日期选择器&#xff0c;我希望能通过其他方式触发日期选择器同时把input输入框去掉…

【IoT-NTN】系统消息SIB32信令分析

3GPP卫星通信发展迅速&#xff0c; TS36.331 R17中新增SIB32携带星历信息&#xff0c;本文对SIB32的信令内容进行了分析。 SystemInformationBlockType32 概述 SystemInformationBlockType32 是用于提供预测非连续覆盖的卫星辅助信息的系统信息块。这个信息块仅在非地面网络&…

初学者如何快速入门Python(详细攻略),从0到精通,不信你学不会!

近年来&#xff0c;人工智能领域的飞速发展极大地改变了各个行业的面貌。当前最新的技术动态&#xff0c;如大型语言模型和深度学习技术的发展&#xff0c;展示了深度学习和机器学习技术的强大潜力&#xff0c;成为推动创新和提升竞争力的关键。特别是PyTorch&#xff0c;凭借其…

刚面试完的前端面试题

今天晚上参加了一场长达40多分钟的技术面。我觉得面试官非常专业&#xff0c;问的问题也都是很棒的&#xff01;自己很多知识都需要学习。所以我决定回想并记录下来。回答不对的地方欢迎大家指正&#xff01; 我自己在小本本上回忆出来的大概就是26道题。后期我会持续更新我学习…