LeetCode从入门到超凡(五)深入浅出---位运算

news2024/11/18 21:31:35

head-bar

引言

大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档;本文主要讲解 位运算算法。💕💕😊


一、 位运算简介

1.什么是位运算?

① 位运算的定义

位运算(Bit Operation)是指直接对整数的二进制位进行操作的运算。在计算机内部,所有的数据都是以二进制形式存储的,因此位运算可以直接操作这些二进制位,从而实现一些高效的计算。

② 优势:提高程序性能

位运算的优势在于其高效性。由于位运算是直接对二进制位进行操作,不需要进行复杂的数值转换,因此在某些情况下,使用位运算可以显著提高程序的性能。例如,在处理大量数据或需要频繁进行位操作的场景中,位运算可以大大减少计算时间

2.二进制数的基本概念

① 二进制数的表示方法

二进制数(Binary)是由 01 两个数码组成的数。在计算机中,所有的数据最终都会被转换为二进制形式进行存储和处理。

② 二进制数的位(Bit)

在二进制数中,每一个 01 被称为一个位(Bit)。位是二进制数的最小单位,多个位组合在一起可以表示更大的数值。

① 二进制与十进制的区别
  • 十进制:由 09 共 10 个数码组成,进位规则是“满十进一”。例如,7 + 2 = 99 + 2 = 11
  • 二进制:由 01 两个数码组成,进位规则是“逢二进一”。例如,1 + 0 = 11 + 1 = 10
② 二进制的进位规则:逢二进一

在二进制中,当某一位的数值达到 2 时,就会向高位进一。例如:

  • 1 + 0 = 1
  • 1 + 1 = 10(相当于十进制的 2
  • 10 + 1 = 11(相当于十进制的 3
③ 示例:二进制数的加法

让我们通过一个简单的例子来理解二进制数的加法:

  101 (二进制)
+ 011 (二进制)
------
 1000 (二进制)

在这个例子中:

  • 最低位 1 + 1 = 10,结果是 0,进位 1
  • 第二位 0 + 1 + 进位 1 = 10,结果是 0,进位 1
  • 第三位 1 + 0 + 进位 1 = 10,结果是 0,进位 1
  • 最高位只有进位 1,结果是 1

最终结果是 1000,即十进制的 8


3. 二进制数的转换

① 二进制转十进制

转换方法:按权展开

将二进制数转换为十进制数的方法是按权展开。每一位的权值是 2 的幂次方,从右到左依次为 2^0, 2^1, 2^2, …。

示例:二进制数 01101010 转换为十进制

二进制数:01101010
按权展开:
0 * 2^7 + 1 * 2^6 + 1 * 2^5 + 0 * 2^4 + 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0
= 0 + 64 + 32 + 0 + 8 + 0 + 2 + 0
= 106

所以,二进制数 01101010 转换为十进制数是 106

② 十进制转二进制

转换方法:除二取余,逆序排列

将十进制数转换为二进制数的方法是“除二取余,逆序排列”。具体步骤如下:

  1. 将十进制数不断除以 2,记录每次的余数。
  2. 将所有余数逆序排列,得到二进制数。

示例:十进制数 106 转换为二进制

106 ÷ 2 = 53 余 0
 53 ÷ 2 = 26 余 1
 26 ÷ 2 = 13 余 0
 13 ÷ 2 = 6  余 1
  6 ÷ 2 = 3  余 0
  3 ÷ 2 = 1  余 1
  1 ÷ 2 = 0  余 1

将余数逆序排列,得到 1101010。由于二进制数通常从高位开始,所以最终结果是 01101010

所以,十进制数 106 转换为二进制数是 01101010

通过这些步骤,我们可以理解位运算的基本概念和二进制数的转换方法。接下来,我们将深入探讨位运算的具体操作。

二、位运算基础操作

1.按位与运算(AND)

① 运算符:&

按位与运算使用符号 & 表示。它是一种双目运算符,即需要两个操作数。

② 运算规则

按位与运算的规则是:只有当两个二进位都为 1 时,结果位才为 1。否则,结果位为 0

具体规则如下:

  • 1 & 1 = 1
  • 1 & 0 = 0
  • 0 & 1 = 0
  • 0 & 0 = 0
③ 示例

让我们通过一个具体的例子来理解按位与运算:

  01111100
& 00111110
----------
  00111100

逐位进行与运算:

  • 第 1 位:0 & 0 = 0
  • 第 2 位:0 & 1 = 0
  • 第 3 位:1 & 1 = 1
  • 第 4 位:1 & 1 = 1
  • 第 5 位:1 & 1 = 1
  • 第 6 位:1 & 1 = 1
  • 第 7 位:1 & 1 = 1
  • 第 8 位:0 & 0 = 0

最终结果是 00111100

2. 按位或运算(OR)

① 运算符:|

按位或运算使用符号 | 表示。它也是一种双目运算符,需要两个操作数。

② 运算规则

按位或运算的规则是:只要有一个二进位为 1,结果位就为 1。否则,结果位为 0

具体规则如下:

  • 1 | 1 = 1
  • 1 | 0 = 1
  • 0 | 1 = 1
  • 0 | 0 = 0
③ 示例:

下面通过一个具体的例子来理解按位或运算:

  01001010
| 01011011
----------
  01011011

逐位进行或运算:

  • 第 1 位:0 | 1 = 1
  • 第 2 位:1 | 1 = 1
  • 第 3 位:0 | 0 = 0
  • 第 4 位:0 | 1 = 1
  • 第 5 位:1 | 1 = 1
  • 第 6 位:0 | 0 = 0
  • 第 7 位:1 | 1 = 1
  • 第 8 位:0 | 1 = 1

最终结果是 01011011

3. 按位异或运算(XOR)

① 运算符:^

按位异或运算使用符号 ^ 表示。它也是一种双目运算符,需要两个操作数。

② 运算规则

按位异或运算的规则是:对应的两个二进位相异时,结果位为 1,相同时为 0

具体规则如下:

  • 1 ^ 1 = 0
  • 1 ^ 0 = 1
  • 0 ^ 1 = 1
  • 0 ^ 0 = 0
③ 示例

下例中我们理解按位异或运算:

  01001010
^ 01000101
----------
  00001111

逐位进行异或运算:

  • 第 1 位:0 ^ 1 = 1
  • 第 2 位:1 ^ 0 = 1
  • 第 3 位:0 ^ 0 = 0
  • 第 4 位:0 ^ 0 = 0
  • 第 5 位:1 ^ 0 = 1
  • 第 6 位:0 ^ 1 = 1
  • 第 7 位:1 ^ 0 = 1
  • 第 8 位:0 ^ 1 = 1

最终结果是 00001111

4. 取反运算(NOT)

① 运算符:~

取反运算使用符号 ~ 表示。它是一种单目运算符,只需要一个操作数。

② 运算规则

取反运算的规则是:将 1 变为 00 变为 1

具体规则如下:

  • ~0 = 1
  • ~1 = 0
③ 示例

让我们通过一个具体的例子来理解取反运算:

~01101010
----------
10010101

逐位进行取反运算:

  • 第 1 位:~0 = 1
  • 第 2 位:~1 = 0
  • 第 3 位:~1 = 0
  • 第 4 位:~0 = 1
  • 第 5 位:~1 = 0
  • 第 6 位:~0 = 1
  • 第 7 位:~1 = 0
  • 第 8 位:~0 = 1

最终结果是 10010101

5. 左移运算(SHL)

① 运算符:<<

左移运算使用符号 << 表示。它是一种双目运算符,需要一个操作数和一个移位次数。

② 运算规则

左移运算的规则是:将二进制数的各个二进位全部左移若干位,高位丢弃,低位补 0

③ 示例:01101010 左移 1

让我们通过一个具体的例子来理解左移运算:

01101010 << 1
----------
11010100

逐位进行左移运算:

  • 第 1 位:0 移出,高位丢弃
  • 第 2 位:1 移到第 1 位
  • 第 3 位:1 移到第 2 位
  • 第 4 位:0 移到第 3 位
  • 第 5 位:1 移到第 4 位
  • 第 6 位:0 移到第 5 位
  • 第 7 位:1 移到第 6 位
  • 第 8 位:0 移到第 7 位
  • 低位补 0

最终结果是 11010100

6. 右移运算(SHR)

① 运算符:>>

右移运算使用符号 >> 表示。它也是一种双目运算符,需要一个操作数和一个移位次数。

② 运算规则

右移运算的规则是:将二进制数的各个二进位全部右移若干位,低位丢弃,高位补 0

③ 示例:01101010 右移 1

让我们通过一个具体的例子来理解右移运算:

01101010 >> 1
----------
00110101

逐位进行右移运算:

  • 第 8 位:0 移出,低位丢弃
  • 第 7 位:1 移到第 8 位
  • 第 6 位:0 移到第 7 位
  • 第 5 位:1 移到第 6 位
  • 第 4 位:0 移到第 5 位
  • 第 3 位:1 移到第 4 位
  • 第 2 位:1 移到第 3 位
  • 第 1 位:0 移到第 2 位
  • 高位补 0

最终结果是 00110101


三、 位运算的应用

1. 位运算的常用操作

① 判断整数奇偶

原理:通过与 1 进行按位与运算

判断一个整数是奇数还是偶数,可以通过与 1 进行按位与运算。如果结果为 0,则该数为偶数;如果结果为 1,则该数为奇数。

示例:判断 x 是奇数还是偶数

def is_even(x):
    return (x & 1) == 0

def is_odd(x):
    return (x & 1) == 1

# 示例
x = 10
print(f"{x} 是偶数吗?", is_even(x))  # 输出:True
print(f"{x} 是奇数吗?", is_odd(x))   # 输出:False

思维流程
uml1

② 二进制数选取指定位

原理:使用按位与运算

要选取二进制数中的某几位,可以使用按位与运算。通过构造一个掩码(mask),掩码中对应选取位置为 1,其余位置为 0,然后与原二进制数进行按位与运算。

示例:取二进制数 01101010 的末尾 4

def get_last_n_bits(x, n):
    mask = (1 << n) - 1
    return x & mask

# 示例
x = 0b01101010
n = 4
result = get_last_n_bits(x, n)
print(f"二进制数 {bin(x)} 的末尾 {n} 位是 {bin(result)}")  # 输出:0b1010

思维流程
uml2

③ 将指定位设置为 1

原理:使用按位或运算

要将二进制数中的某几位设置为 1,可以使用按位或运算。通过构造一个掩码,掩码中对应选取位置为 1,其余位置为 0,然后与原二进制数进行按位或运算。

**示例:将二进制数 01101010 的末尾 4 位设置为 **1

def set_last_n_bits(x, n):
    mask = (1 << n) - 1
    return x | mask

# 示例
x = 0b01101010
n = 4
result = set_last_n_bits(x, n)
print(f"二进制数 {bin(x)} 的末尾 {n} 位设置为 1 后是 {bin(result)}")  # 输出:0b1111
④ 反转指定位

原理:使用按位异或运算

要反转二进制数中的某几位,可以使用按位异或运算。通过构造一个掩码,掩码中对应选取位置为 1,其余位置为 0,然后与原二进制数进行按位异或运算。

示例:将二进制数 01101010 的末尾 4 位反转

def invert_last_n_bits(x, n):
    mask = (1 << n) - 1
    return x ^ mask

# 示例
x = 0b01101010
n = 4
result = invert_last_n_bits(x, n)
print(f"二进制数 {bin(x)} 的末尾 {n} 位反转后是 {bin(result)}")  # 输出:0b1100
⑤ 交换两个数

原理:使用按位异或运算

通过按位异或运算可以实现两个数的交换,而无需额外的变量。

示例:交换 ab 的值

def swap_numbers(a, b):
    a ^= b
    b ^= a
    a ^= b
    return a, b

# 示例
a, b = 10, 20
a, b = swap_numbers(a, b)
print(f"交换后 a = {a}, b = {b}")  # 输出:a = 20, b = 10

思维流程图

uml3

⑥ 将二进制最右侧为 1 的二进位改为 0

**原理:使用 **X & (X - 1)

要将二进制数中最右侧为 1 的二进位改为 0,可以使用 X & (X - 1) 操作。

**示例:将 01101100 最右侧的 1 改为 **0

def clear_rightmost_bit(x):
    return x & (x - 1)

# 示例
x = 0b01101100
result = clear_rightmost_bit(x)
print(f"二进制数 {bin(x)} 最右侧的 1 改为 0 后是 {bin(result)}")  # 输出:0b1101000
⑦ 计算二进制中二进位为 1 的个数

原理:使用 X & (X - 1) 统计次数

通过不断使用 X & (X - 1) 操作,可以将二进制数中最右侧为 1 的二进位改为 0,直到所有位都为 0。统计操作次数,即可得到二进制中 1 的个数。

示例:计算 011011001 的个数

def count_ones(x):
    count = 0
    while x:
        x &= (x - 1)
        count += 1
    return count

# 示例
x = 0b01101100
result = count_ones(x)
print(f"二进制数 {bin(x)} 中 1 的个数是 {result}")  # 输出:4

思维流程
uml4

⑧ 判断某数是否为 2 的幂次方

**原理:使用 **X & (X - 1) == 0

判断一个数是否为 2 的幂次方,可以通过 X & (X - 1) == 0 来实现。如果结果为 0,则该数是 2 的幂次方;否则,不是。

示例:判断 4 是否为 2 的幂次方

def is_power_of_two(x):
    return (x & (x - 1)) == 0

# 示例
x = 4
result = is_power_of_two(x)
print(f"{x} 是 2 的幂次方吗? {result}")  # 输出:True

思维流程

uml5

2. 位运算的常用操作总结

① 常用操作列表
功能位运算符示例
判断整数奇偶&(x & 1) == 0
选取指定位&x & ((1 << n) - 1)
将指定位设置为 1``
反转指定位^x ^ ((1 << n) - 1)
交换两个数^a ^= b; b ^= a; a ^= b;
将最右侧 1 改为 0&x & (x - 1)
计算 1 的个数&while x: x &= (x - 1); count += 1
判断是否为 2 的幂次方&(x & (x - 1)) == 0

3. 二进制枚举子集

① 二进制枚举子集简介

子集的概念

子集是指一个集合中的任意元素都是另一个集合的元素。例如,集合 {1, 2, 3} 的子集包括 {}{1}{2}{3}{1, 2}{1, 3}{2, 3}{1, 2, 3}

二进制枚举子集的原理

对于一个元素个数为 n 的集合 S,可以用一个长度为 n 的二进制数来表示其子集。每一位对应集合中的一个元素,1 表示选取该元素,0 表示不选取该元素。通过枚举 02^n - 1 的所有二进制数,可以得到集合 S 的所有子集。

② 二进制枚举子集代码

代码实现:枚举集合 S 的所有子集

def subsets(S):
    n = len(S)
    sub_sets = []
    for i in range(1 << n):
        sub_set = []
        for j in range(n):
            if i & (1 << j):
                sub_set.append(S[j])
        sub_sets.append(sub_set)
    return sub_sets

# 示例
S = [1, 2, 3]
result = subsets(S)
print(f"集合 {S} 的所有子集是 {result}")  # 输出:[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

思维流程图

uml6

Ok,今天我们就学习到这!😎👌


相关链接

  • 项目地址:LeetCode-CookBook
  • 相关文档:专栏地址
  • 作者主页:GISer Liu-CSDN博客

thank_watch

如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star🌟也可以😂.

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

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

相关文章

【腾讯元宝-免费论文精读】

【腾讯元宝-免费论文精读】 1. 腾讯混元大模型2. 论文精读过程3. 总结&#xff1a; 1. 腾讯混元大模型 由腾讯研发的大语言模型&#xff0c;具备强大的中文创作能力&#xff0c; 复杂语境下的逻辑推理能力&#xff0c;以及可靠的任务执行能力 腾讯元宝&#xff1a;轻松工作&am…

Django对接支付宝沙箱环境(2024年9月新测有效)

1、申请沙箱环境 #需要填一些个人信息 https://opendocs.alipay.com/ 2、使用支付宝登入&#xff0c;并进入控制台&#xff0c;进入开发者工具推荐-->沙箱 3、获取基本信息 主要是APPID,和支付宝网关地址 4、生成应用私钥和应用公钥和支付宝公钥 上面的接口加签方式选择…

【Linux 22】生产者消费者模型

文章目录 &#x1f308; 一、生产者消费者模型⭐ 1. 生产者消费者模型的概念⭐ 2. 生产者消费者模型的特点⭐ 3. 生产者消费者模型的优点 &#x1f308; 二、基于阻塞队列的生产消费模型⭐ 1. 阻塞队列概念⭐ 2. 模拟实现基于阻塞队列的生产消费模型 &#x1f308; 三、POSIX 信…

Kubernetes云原生存储解决方案之 Rook Ceph实践探究

Kubernetes云原生存储解决方案之 Rook Ceph实践探究 除了手动部署独立的 Ceph 集群并配置与Kubernetes进行对接外&#xff0c;Rook Ceph 支持直接在 Kubernetes 集群上部署 Ceph 集群。 通过Rook Ceph云原生存储编排平台&#xff0c;使得 Kubernetes 集群中启用高可用的 Ceph…

PHP 基础语法详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【有啥问啥】多目标跟踪SORT算法原理详解

多目标跟踪SORT算法原理详解 引言 多目标跟踪&#xff08;Multiple Object Tracking, MOT&#xff09;是计算机视觉领域的一个重要研究方向&#xff0c;广泛应用于视频监控、自动驾驶、人机交互等多个领域。其核心任务是在视频序列中持续、准确地识别和定位多个目标。SORT&am…

爬虫入门之爬虫原理以及请求响应

爬虫入门之爬虫原理以及请求响应 爬虫需要用到的库, 叫requests. 在导入requests库之前, 需要安装它, 打开cmd: 输入pip install 库名 pip install requests后面出现successful或requirement already就说明已经下载成功了!!! 下载出现的问题: 1.有报错或者是下载慢 修改镜像…

计算机的错误计算(一百零八)

摘要 回复网友来信&#xff0c;接前一节本节再谈多项式的错误计算。 例1. 计算 若在Visual Studio 2010中用C#编程计算&#xff1a; using System; using System.Collections.Generic; using System.Linq; class Program { static void Main(){ long part1 946495 * (…

Redis缓存双写一致性笔记(下)

Redis和Canal结合使用是一种常见的解决方案&#xff0c;用于确保MySQL数据库中的更改实时同步到Redis缓存中&#xff0c;从而保持数据的一致性。 这种同步机制虽然能够实现近乎实时的数据同步&#xff0c;但可能会有轻微的延迟&#xff0c;因此它更适合对数据一致性要求不是特…

STM32 DMA+AD多通道

单片机学习&#xff01; 目录 一、DMA配置步骤 二、ADC配置步骤 三、DMAAD多通道框图 四、DMAAD多通道函数设计详细步骤 4.1 开启RCC时钟 4.2 配置GPIO 4.3 配置多路开关 4.4 结构体初始化ADC 4.5 DMA参数初始化配置 4.5.1 外设站点的三个参数 4.5.2 存储器站点的三个…

Tomcat 调优技巧(Tomcat Tuning Tips)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

IDEA关联Tomcat

一、Tomcat服务器 web服务器,就是运行web项目的容器 即运行java代码的一个容器 webapp(web应用程序) --> 就是我们写的javaweb项目 Tomcat 是Apache 软件基金会&#xff08;Apache Software Foundation&#xff09;下的一个核心项目&#xff0c;免费开源、并支持Servlet 和J…

yolov8/9/10模型在垃圾分类检测中的应用【代码+数据集+python环境+GUI系统】

yolov8/9/10模型在垃圾分类检测中的应用【代码数据集python环境GUI系统】 yolov8/9/10模型在垃圾分类检测中的应用【代码数据集python环境GUI系统】 背景意义 随着计算机视觉技术和深度学习算法的快速发展&#xff0c;图像识别、对象检测、图像分割等技术在各个领域得到了广泛…

DL_语义分割(学习笔记)

文章目录 图像分割1 常见分类1.1 语义分割1.2 实例分割1.3 全景分割 2 语义分割2.1 模型评价指标2.2 常用数据集2.3 转置卷积2.4 膨胀卷积2.5 感受野2.6 双线性插值2.7 FCN 图像分割 1 常见分类 1.1 语义分割 定义&#xff1a;【只判断类别&#xff0c;无法区分个体】 语义分…

Matlab实现麻雀优化算法优化回声状态网络模型 (SSA-ESN)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1内容介绍 麻雀搜索算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种新兴的群体智能优化算法&#xff0c;灵感来源于麻雀的觅食行为及其在面临危险时的预警机制。SSA通过模拟麻雀的这些自然行为来寻找问题…

[Docker学习笔记]利用Dockerfile创建镜像

Dockerfile 指令 指令作用from继承基础镜像maintainer镜像制作者信息(可缺省)run用来执行shell命令expose暴露端口号cmd启动容器默认执行的命令entrypoint启动容器真正执行的命令volume创建挂载点env配置环境变量add复制文件到容器copy复制文件到容器workdir设置容器的工作目录…

蓝卓亮相中国工博会,打造以数据驱动的智能工厂

9月28日&#xff0c;以“工业聚能&#xff0c;新质领航”为主题的第24届中国国际工业博览会&#xff08;以下简称“工博会”&#xff09;在国家会展中心&#xff08;上海&#xff09;圆满拉下帷幕。本届工博会共设9大专业展区&#xff0c;吸引了来自全球28个国家和地区的2600余…

针对考研的C语言学习(定制化快速掌握重点4)

typedef的使用 简化变量类型 逻辑结构 集合结构&#xff1a;无关系 线性结构&#xff1a;一对一 树形结构&#xff1a;一对多 图形结构&#xff1a;多对多 存储结构 顺序存储和链式存储&#xff08;考代码&#xff09; 顺序存储优点&#xff1a;1.可以实现随机存取。2.…

针对考研的C语言学习(定制化快速掌握重点5)

顺序表 特点&#xff1a; 写代码主要就是增删改查&#xff01;&#xff01;&#xff01; 写代码的边界性非常重要以及考研插入和删除的位置都是从1开始&#xff0c;而数组下标是从0开始 【注】下标和位置的关系 线性表最重要的是插入和删除会涉及边界问题以及判断是否合法 …

【Spring Boot 入门二】Spring Boot中的配置文件 - 掌控你的应用设置

一、引言 在上一篇文章中&#xff0c;我们开启了Spring Boot的入门之旅&#xff0c;成功构建了第一个Spring Boot应用。我们从环境搭建开始&#xff0c;详细介绍了JDK的安装以及IDE的选择与配置&#xff0c;然后利用Spring Initializr创建了项目&#xff0c;分析了项目结构&am…