【Hello Algorithm】异或法

news2024/9/23 21:26:27

作者:@小萌新
专栏:@算法
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:介绍算法中的异或法

异或法

    • 异或的概念
    • 异或的两个性质
    • 题目一 不使用额外变量交换两个数字
    • 题目二 出现奇数次的数字
    • 题目三 如何从一个整型数字中提取出右边的第一个1 (以整数类型表示)
    • 题目四 一个数组中两个数出现了一次 其他数出现了两次 如何找出这两个出现一次的数字
    • 题目五 一个数组中一个数出现了一次 其他数出现了三次 要求你找出出现一次的这个数

异或的概念

异或在书中一般是这么解释的

“ ^ ”的异或指的是二进制中 对应的对应二进制位相同时异或为零 相异时异或为一

但是我这里推荐一种很巧妙的记忆方式

异或就是二进制位无进位相加

我们下面使用一个例子来让大家更好的理解这句话

在这里插入图片描述

仔细观察 在上图中我们将对应位的二进制相加

  • 如果有进位 则舍去进位
  • 如果无进位 则不变

最后就得到了我们异或的结果

异或的两个性质

  1. 0 ^ N = N
  2. N ^ N = 0

我们一个个推导

首先来看性质一

0的所有二进制位都是0 而任何数加上0都不变 换一种说法也就是无进位 所以说该位不会改变

那么既然所有位都不会改变这个数当然也不会改变

其次是性质二

两个相同的数异或结果肯定为0

首先我们知道两个相同的数 每一位的二进制位肯定都相同 而二进制位一共就两种情况 0或1

  • 如果是0 则两位相加之后还是0 不变
  • 如果是1 则两位相加之后进位1 舍弃掉 所以说该位还是0

综上 我们就可以推出两个相同的数异或之后为0

最后根据这两个性质我们还能够推出一个重要的性质

如果一堆数字异或 那么无论我们怎么改变这堆数据的顺序 最后的结果都不会变

下面是简单的证明

首先不管是什么数字 它二进制的每个位肯定是确定的 不是0就是1

我们将每个位的所有数字累加起来只有两种结果 奇数还是偶数

我们上面说过 异或实际上就是无进位相加 如果是偶数模上2之后最后的结果会变成0 (进位都被我们舍弃了)

如果是技术模上2之后最后的结果会变成1 (进位都被我们舍弃了)

所以说 不管我们怎么改变顺序 每个位的数字都是不变的 所以说 如果一堆数字异或 那么无论我们怎么改变这堆数据的顺序 最后的结果都不会变

题目一 不使用额外变量交换两个数字

题目要求 不使用额外变量交换两个数字

交换两个数字的值最常用的方法就是申请一个临时变量 然后我们利用这个临时变量进行两个数字的交换

代码表示如下

  void swap(int& x , int& y)
  {
    int temp = x;
    x = y;
    y = temp;
  }

但是按照这个题目的意思 我们不能申请临时变量 这个temp是不允许使用的

这里我们可以使用算数的性质来解决这个问题

  void swap2(int& x , int& y)                                                                                  
  {                   
     x = x + y;
     y = x - y;
     x = x - y;
  }

下面是运行的代码和结果

void swap2(int& x , int& y)    
{    
   x = x + y;    
   y = x - y;    
   x = x - y;    
}    
    
    
int main()    
{    
  int a = 10;    
  int b = 20;    
  swap2(a , b);    
    
  cout << "a: " << a << endl;    
  cout << "b: " << b << endl;                                                                                  
  return 0;    
} 

结果如下

在这里插入图片描述

但是这种解法有一种缺点 有可能会数据溢出

 x = x + y;   

如果此时我们的x和y特别大 超过了数据类型所能容纳的数字 就会造成数据溢出从而发生错误 所以说这种解法就是有缺陷的

此时为了防止数据溢出的情况我们就可以使用异或法来解决问题

代码和结果如下

void swap3(int& x , int& y)    
{    
  x = x ^ y;    
  y = x ^ y;    
  x = x ^ y;    
}    
    
int main()    
{    
  int a = 10;    
  int b = 20;    
  swap3(a , b);                                                                          
    
  cout << "a: " << a << endl;    
  cout << "b: " << b << endl;    
  return 0;    
}  

结果如下
在这里插入图片描述

同时我们的异或法只是改变数据的二进制位的情况 所以说数据绝对不会出现溢出

下面是原理解释 为什么进行三次异或就能够交换两个数的值

在这里插入图片描述

注意:

但是这里需要注意的是 如果A值和B值指向同一个空间 此时就不能使用异或法来交换两个值 因为异或之后两个数字百分百变为0

首先我们要知道 两个相同的数字异或是肯定为0的

我们上面进行了三次异或操作 每次异或操作之后只有一个数字变化了

但是如果两个值指向同一个空间 则进行一次异或操作之后该空间的值就会变为0 即两个数字都改变了

所以说使用该方法的时候千万要注意 两个值在计算机中不能使用同一空间

题目二 出现奇数次的数字

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

该题目出现于lc136题 题目原文和测试用例如下

在这里插入图片描述

此时它要求我们找出一个数组中那个只出现一次的数字 我们第一时间就应该想到异或法

我们异或法是不是有一个性质

  N ^ N = 0

也就是说两个相同的数字异或 它们的值为0 当然我们也可以推广一下 偶数个相同的数字异或 它们的值为0

也就是说题目中所有出现偶数次的数字在异或之后都会变成0

然后根据异或法的第二个性质

  0 ^ N = N

我们最后那个出现一次的数异或上0 就能得到我们的最终结果了

代码表示如下

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

        return eor;
    }
};

运行结果如下

在这里插入图片描述

题目三 如何从一个整型数字中提取出右边的第一个1 (以整数类型表示)

当然遍历读取肯定是可以的

我们这里直接给出一个公式

  int a = 10;
  int rightone = a & (-a);

如果大家感兴趣可以自己推出下这个公式 (注意计算机中的原反补码)

因为10的二进制是 0000 1010 所以说rightone计算出的结果肯定是 0000 0010

如何计算一个32位无符号数中1的个数

该题我们可以使用我们上面学到的技巧 不断的提取该数字最右边的1

之后不断地将最右边的1消除就可以了 (取反异或)

代码和运行结果如下

class Solution {
public:
    int hammingWeight(uint32_t n) 
    {
        if (n == 0)
        {
            return 0;
        }

        int count = 0;
        while (n != 0)
        {
            size_t rightone = n & (-n);
            n &= ~rightone;
            count++;
        }

        return count;
    }
};

在这里插入图片描述

当然其实我们还有一种更巧妙的解法 我们这里直接给出代码

class Solution {
public:
    int hammingWeight(uint32_t n) 
    {
        if (n ==0)
        {
            return 0;
        }

        int count = 0;
        while( n &= (n - 1))
        {
            count++;
        }
        return ++count;
    }
};

这里其实就是运用了二进制的特性和位操作的特性 大家可以通过自己手动画图的方式来加深下了解

题目四 一个数组中两个数出现了一次 其他数出现了两次 如何找出这两个出现一次的数字

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

这是lc上的剑指offer第56题 题目原文和测试用例如下

在这里插入图片描述
该题目其实和我们的题目二有些类似

我们通过异或之后能够将所有出现偶数次的数字变为0 最后的结果实际上就变为了两个出现一次的数字相互异或的结果

现在我们要想的就是如果通过该结果将该数组分为两部分 (因为这样子就变为了两个问题二 这样子就好解决多了)

根据异或的特性 异或之后的结果一定不为0 (特性2 两个相同的数字异或才为0 可以推出不同的数异或之后不为0 ) 所以说它们的二进制位上一定有为1的位

而这两个数在该二进制位上的位肯定是不同的 一个为0 一个为1 关于这一点 我们在前面已经推断过了

在这里插入图片描述
所以说我们就可以使用该二进制上是0还是1 将这两个数字分开 并且将整个数组分为两部分

并且我们可以保证在每一部分中只有一个数出现了一次 其他数都出现了两次 (两个相同的数二进制位肯定相同 所以说肯定出现在同一组中)

之后我们再重复题目二的解法就好了 代码和运行结果如下

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) 
    {
        int eor = 0;
        for (auto x : nums)
        {
            eor ^= x;
        }

        int rightone = eor & (-eor); 
        

        int leftnum = eor; 
        for (auto x : nums)
        {
            if ((x & rightone) == 0)
            {
                leftnum ^= x; 
            }
        }
        int rightnum = eor ^ leftnum;

        vector<int> ans;
        ans.push_back(leftnum);
        ans.push_back(rightnum);

        return ans;
    }
};

运行结果如下

在这里插入图片描述

题目五 一个数组中一个数出现了一次 其他数出现了三次 要求你找出出现一次的这个数

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

这是lc上剑指offer的第56题二 题目原文和测试用例如下

在这里插入图片描述

这道题其实就有点超出异或的范畴了 不过出现在异或法的同源题目上我们就讲解下

我们首先假设 所有的数字都出现了三次 那么在比特位上会是什么情况呢?

是不是所有的比特位都能够被3整除啊

那么我们现在转化下思路 加上那个只出现一次的数字

是不是就说明不能被3整除的比特位就肯定是那个数字出现的地方

那么我们遍历32个比特位 看哪些位不能被3整除 是不是就能够找出那个只出现一次的数字啊

其实这个题目还能做延申 如果一个数字出现 M 次 (1 < M < K ) 其余数字都出现 K次 我们找出这个出现M次数字的方式和这道题的解法是一模一样的

代码和演示结果如下

class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
        // 设立一个32位的数组 对应的每个数字上如果该位是1就+1
        // 如果该位是0就什么都不做 
        vector<int> ans;
        ans.reserve(32);

        for(auto x : nums)
        {
            for (int i = 0; i < 32; i++)
            {
                if (1 & (x >> i))
                {
                    ans[i]++;
                }
            }
        }


        int result = 0;
        for (int i = 0; i < 32; i++)
        {
            if (ans[i] % 3 )
            {
                result += (1 << i);
            }
        }

        return result;
    }
};

在这里插入图片描述

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

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

相关文章

石油化工行业室内外高精度人员定位系统解决方案

石油化工行业是高危行业&#xff0c;很容易发生安全事故&#xff0c;对于石化企业来说&#xff0c;加强人员的安全管控非常有必要。我们可以通过人员定位技术&#xff0c;提升石化企业安全管理水平。下面给大家分享石油化工行业室内外高精度人员定位系统解决方案。 方案概述 石…

BERT原理Fine TuningBert变种

文章目录 BERT原理训练时的任务任务一任务二任务二的改进 模型的输入 BERT - Fine Tuning单个句子的预测类序列标注类Q&A类seq2seq&#xff1f; BERT 变种Transformer-XLXLNetAutoregressive Language ModelDenoising Auto-Encoder乱序Two-Stream Attention与Transformer-X…

RocketMQ双主双从环境搭建

环境要求 64位操作系统&#xff0c;推荐 Linux/Unix/macOS 64位 JDK 1.8 服务器准备 准备4台服务器两台master两台slave&#xff0c;如果服务器紧凑&#xff0c;则至少需要两台服务器相互master-slave IP HOSTS 172.*******.120 rocketmq-nameserver1 rocketmq-master1 …

ElasticSearch小计

1、ElasticSearch简介 1.1、ElasticSearch&#xff08;简称ES&#xff09; Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎。能够达到近实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速&#xff0c;安装使用方便。客户端支持Java、.NET&#x…

Class 00 - 学习编程的方法不同职业所使用的编程语言

Class 00 - 学习编程的方法&不同职业所使用的编程语言 学习编程的方法什么是编程&#xff1f;不同职业所使用的编程语言数据分析网页设计移动应用开发Web应用开发游戏开发 Tips&#xff1a;学习编程语言的技巧 从电子表格到 SQL 再到 R电子表格、SQL和R:一个比较 学习编程的…

根据端口查询该程序占用的内存 gpu

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、如何根据端口号查询该程序的占用内存…

JavaScript语法基础

js学习路线 数据判度 1&#xff0c;类型分类undefined,Null,Number,String,Boolean 2,类型判断typeof操作符 var sTemp “tesst” 例如alert(typeos sTemp); //输出String alert(typeof 23);//输出number 3&#xff0c;instanceof操作符&#xff1a;用于判断一个引用类型属于…

【C++】C++中的继承

目录 一.继承的概念和定义1.继承的概念2.继承定义2.1定义格式2.2继承关系和访问限定符2.3继承基类成员访问方式的变化 二.基类和派生类对象赋值转换三.继承中的作用域四.派生类的默认成员函数五.继承和友元六.继承与静态成员七.复杂的菱形继承及菱形虚拟继承1.单继承2.多继承3.…

React 中 TypeScript 和装饰器及 Hooks

概念 TypeScript 是强类型语言&#xff0c;相对于JavaScript 弱类型语言&#xff0c;它具有类型检测的功能&#xff0c;扩展了JavaScript 的语法。 TS的安装与执行&#xff1a; //全局安装typescript npm install typescript -g// 第二个因为 本来的node是不可能支持 ts那种民…

2023网络安全学习路线 非常详细 推荐学习

前言&#xff1a;首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 目录&#xff1a; 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有几门&#xff0c;有些人会倒在学习 linux 系统及命令的路上&#xff0c;更多的人会倒在学习语言上&#xff1b; …

SSD系列1——网络结构

SSD系列&#xff1a; SSD系列1——网络结构 SSD系列2——PriorBox SSD系列3——损失计算 SSD网络结构概述 SSD在VGGNet的基础上&#xff0c;增加了4个卷积模块&#xff0c;这些卷积模块获得的特征图具有不同的感受野&#xff0c;可以较好地检测不同尺度的目标。 VGG16 SSD网络…

springboot 断点上传、续传、秒传实现

文章目录 前言一、实现思路二、数据库表对象二、业务入参对象三、本地上传实现三、minio上传实现总结 前言 springboot 断点上传、续传、秒传实现。 保存方式提供本地上传&#xff08;单机&#xff09;和minio上传&#xff08;可集群&#xff09; 本文主要是后端实现方案&…

AI绘画:Lora模型训练完整流程!

关于AI绘画(基于Stable Diffusion Webui)&#xff0c;我之前已经写过三篇文章&#xff0c;分别是 软件安装&#xff0c;基本的使用方法&#xff0c;微调模型LoRA的使用。 整体来说还是比简单的&#xff0c;搞个别人的模型&#xff0c;搞个提示词就出图了。今天来一个有些难度…

推荐11个好用的prompt工具网站(附链接+论文)

同学们&#xff0c;你们prompt是自己苦哈哈码的吗&#xff1f;可别了&#xff0c;有现成的工具为啥不用&#xff1f; 今天我就和大家分享一些好用的prompt工具网站&#xff0c;用熟了ChatGPT、midjourney、stable diffusion能玩起来更爽&#xff01;搜罗了有十几个&#xff0c…

智能汽车实验二(视觉传感器标定)

实验二 视觉传感器标定&#xff08;实验报告&#xff09; 【实验目的】 1、了解开源图像处理库OpenCV的结构&#xff0c;掌握OpenCV的基本使用方法。 2、了解开源图像处理库OpenCV的基本模块功能&#xff0c;掌握常用图像处理方法。 3、掌握摄像机标定算法&#xff0c;学会使用…

Xilinx 7系列FPGA内置ADC

Xilinx 7系列FPGA全系内置了一个ADC&#xff0c;称之为XADC。这个XADC&#xff0c;内部是两个1mbps的ADC&#xff0c;可以采集模拟信号转为数字信号送给FPGA内部使用。 XADC内部可以直接获取芯片结温和FPGA的若干供电电压&#xff08;7系列不包括VCCO&#xff09;&#xff0c;用…

麒麟KylinV10SP1(2203)推荐安装一些硬件监控类软件与使用

目录 前言 1、tlp 电源管理 &#xff08;1&#xff09;查看电池容量、使用量、为Thinkpad设定电池充电开始结束阈值 &#xff08;2&#xff09;查看硬盘比如NVME SSD的型号种类、当前温度、读写量等信息&#xff1b; &#xff08;3&#xff09;查看CPU型号以及频率上下限、…

软件测试简单么,发展前景如何?

随着人工智能时代的到来&#xff0c;IT行业受到了越来越多人的重视。软件测试作为把控软件质量必不可少的环节&#xff0c;其重要性可见一斑。 软件测试可以说是算得上IT行业里相对简单的语言&#xff0c;但是也只是相对哈&#xff0c;如果想学习下去还是要看个人的学习能力的…

软件测试工作内容和职责有哪些

目前&#xff0c;在IT行业中测试的职位数量仅次于开发&#xff0c;可以说是第二大技术就业岗位。然而许多人对测试师工作的理解还停留在&#xff0c;只需要像用户一样使用产品&#xff0c;然后发现有问题提交报告就行了。其实这是极其不准确的&#xff0c;软件测试师在测试产品…

通过Dnspy调试解决powershell使用Install-module指定的转换无效的问题

之前运行Install-module -Name NtObjectManager出现以下错误&#xff1a; PackageManagement\Install-Package : Package NtObjectManager failed to be installed because: 指定的转换无效。 At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.…