从零学算法(LCR 178)

教学过程中,教练示范一次,学员跟做三次。该过程被混乱剪辑后,记录于数组 actions,其中 actions[i] 表示做出该动作的人员编号。请返回教练的编号。
示例 1:
输入:actions = [5, 7, 5, 5]
输出:7
示例 2:
输入:actions = [12, 1, 6, 12, 6, 12, 6]
输出:1

  • 乍一看很简单,某个数字只出现一次,其他数字都出现三次,找出只出现一次的数字。直接用 HashMap 统计每个数字出现的次数即可,不过可以用位运算更快实现。思路为:这些数字都是 32 位整数,那就用一个长度 32 的数组记录每一位 1 的个数。比如三个 1001 和一个 1000,最后会得到数组[4,0,0,3…],表示经统计,第一位为 1 的数字共有 4 个,第四位为 1 的有 3 个,去掉三的倍数个 1,剩下的就是只出现一次的二进制数字每一位的情况。也就从 4003 成了 1000,也就是只出现一次的那个数字。
  •   public int trainingPlan(int[] nums) {int[] res = new int[32];// 统计每一位上的 1 共有几个for(int num : nums){for(int i=0;i<32;i++){res[i] += num & 1;num>>=1;}}// 统计完了就得想办法把这个数组还原成二进制数int ans = 0,m = 3;for(int i=0;i<32;i++){// 从头开始遍历,对 3 取余后的数字,每一位对应的位置应该是 x << i// 比如 res 取余 3 后为 1001,ans 的变化过程为 // 0000 | 1<<0 = 0001// 0001 | 0<<1 = 0001// 0001 | 0<<2 = 0001// 0001 | 1<<3 = 1001// 所以 <<i 就是为了把 0 或 1 放到对应的第几位ans |= res[i]%3<<i;}return ans;}
    
  • 上面一种解法理解起来相对比较容易,根据同样的思路,有更快的解法,不过具体实现有点难以理解。我只能以我的理解来讲解。首先根据上面的思路,一堆 32 位整数在“相加”(懂我说的相加吧,1001+1100=2101,这个相加是统计每一位有几个 1)的过程中由于最后要对 3 取余,所以实际上每一位(注意是每一位)会经历的只有三种状态变化的可能性。规律为 0->1->2->0->1->2…。由于每一位都是同样的变化规律,所以我们先单独分析一位。因为最大会变化成 2 ,所以一个二进制位无法表示,我们用两个二进制位来表示某一位(再次提醒,这个某一位表示的是相加过程中 32 位结果中对应的某一位的统计结果,就是上面解法中的某个 res[i]),我们为这两个二进制位起名为 two 和 one,00,01,10 就分别表示 0,1,2 这三种状态。那么我们重新表示一遍我们状态变化的规律为 00->01->10->00->01->10…。接下来我们先用最原始的判断来表述一遍 one 遇到每个 num 会怎么样(省事点先用 python 了):
  • 在此之前先附上完整的真值表:记录某一位状态的 two one 加上对应位的 n 后的计算结果,two one 共有三种状态,n 为 0 或 1,所以共有 6 种情况
    请添加图片描述
  •   # 其实这也已经简化过了,没有划分每种 two, n, one 最终计算出的对应的 新oneif two==0:if n==0: #记得这个 n 表示的是在 32 位的某一位上的数,因为 one two 也是用来表示某一位相加过程中的状态# 你也可以自己划分一下 if one== 0,if one == 1,会发现最后结果都是 oneone = oneif n==1:one = ~one # one 的相反数if two==1:# 此时 two one 只可能为 10,那么你 n 为 0 ,one 还是保持为 0,n 为 1,我就得变成 00,one 还是 0# 也就是说 n 首先此时必定为 0,并且无论变或不变最后都成了 0one = 0 
    
  • 由于实际上最终状态只会剩下 00 和 01,换言之,因为 two 恒为 0,所以是 one 真正记录了最后的两种状态,所以我们返回 one 就能表示 32 位结果中对应的某一位最后为 0 还是 1
  • 我们通过对上面代码的简化优化成位运算,那么最终我们就得到了每一位的批量计算,因为每一位的计算规则都一样(你需要理解这点,这也是为什么我们先分析单独的一位)。
  • 我们先简化 two 为 0 的情况,列出 n 与 one 的真值表
n  one newone
0  0   0
0  1   1
1  0   1
1  1   0
  • 可以看到符合异或运算,即
  •   if two==0:one = one^nif two==1:one = 0 
    
  • 接着简化,老样子,真值表,这里把 one^n 暂时记作 one
two one newone
0   one  one   
0   one  one
1   one  0
1   one  0
  • 这个就稍复杂一点点,x?0=x, x?1=0,我们知道 x&1=x, x&0=0,但是这里正好相反,是 0 和 1,因为 ~0=1,~1=0 ,那么不难想到了, x&~0=x, x&~1=0,所以我们就得到了 newone = one &~two,把 one^n 再代入回来,我们最终得到了
  • one = (one^n) & ~two
  • 我们还得根据计算出的 one 继续计算出 two,按道理我们是需要根据旧的 one 来加 n 计算出 two,那我们还是一样先真值表,根据旧的 two 和 n 以及 新的 one,推算出旧的 one,然后得到新的 two
    请添加图片描述
  • two old:当前 two 的值
  • 第一行:two 为 0,n 为 1,新的 one 为 1,说明之前的 two,one 为 00,(two,one 我就简称为 x 吧),所以 00+1 = 01,新的 two 为 0;
  • 第二行:two 为 0,n 为 1,新的 one 为 0,说明之前的 x 为 01,01+1=10,新 two 为 1
  • 第三行:two 为 1,n 为 1,新 one 为 0,说明之前的 x 为 10,其实 two 为 1,x 只可能是 10,10+1=00,新 two 为 0
  • 不用我继续写了吧,根据真值表我们先得到最朴素的 6 个 if
  •   if two==0:if n==0:if one == 0:two = twoelif one == 1:two = twoif n==1:if one == 0:two = 1 #~twoif one == 1:two = twoelif two==1:if n==0:if one == 0:two = twoif n==1:if one == 0:two = 0 #~two
    
  • 简化一下,结果还是为 two 的情况可以省略:
  •   if two==0:if n==1:if one == 0:two = 1elif two==1:if n==1:two = 0
    
  • 继续简化:
  •   if two==0:if n==1 && one == 0:two = 1elif two==1:if n==1 && one == 0:two = 0
    
  • 再简化,为了使得 n==1 && one == 0 为 true,你会发现可以转换成 n&~one
  •   if two==0:if n&~one:two = 1elif two==1:if n&~one:two = 0
    
  • 到了这里你不觉得有点眼熟吗,你可以扩充完整来列出真值表
  •   if two==0:if n&~one:two = 1 # 这个 two 就是 newtwoelse:two = 0elif two==1:if n&~one:two = 0else:two = 1
    
  • 然后得到了:
two n&~one newtwo
0   0      0   
0   1  	   1
1   0      1
1   1      0
  • 这不就是异或吗,所以 two = two ^ (n&~one),因为 & 优先级高于 ^ 所以小括号可以省略
  • 最终代码如下
  •   public int trainingPlan(int[] nums) {// 因为是批量位运算,所以用了 ones 而不是 oneint twos = 0, ones = 0;for(int n:nums){ones = (ones^n) & ~twos;twos = twos ^ n & ~ones;}return ones;}
    
  • 我只有一个疑问,为什么别人的解法最终是:
  •   public int trainingPlan(int[] nums) {int twos = 0, ones = 0;for(int n:nums){ones = ones^n & ~twos;twos = twos ^ n & ~ones;}return ones;}
    

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

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

相关文章

LED显示屏主要由哪些部件组成?

LED显示屏是一种广泛用于信息显示和广告宣传的设备&#xff0c;通常由以下几个主要部件组成&#xff1a; LED模块&#xff1a;LED显示屏的核心部件是LED模块&#xff0c;它包括了许多小的LED灯珠&#xff0c;这些LED灯珠可以发光。LED模块的大小和密度决定了显示屏的分辨率和亮…

向《华为人》学习企业内刊的栏目设置和好故事撰写指南

昨天华研荟介绍了企业内刊是否需要办&#xff0c;如何办的有价值。今天给大家介绍具体的企业内刊栏目设置。 “它山之石&#xff0c;可以攻玉。”我们今天不谈理论&#xff0c;我们从实践中学习&#xff0c;来看看华为这座高山是如何做的&#xff0c;我们从华为的内刊《华为人…

位运算符与高级操作

位运算符与高级操作 运算符 高级操作 左移实现乘法 左移n位等价于乘以2的n次方 int x; x 2; x x << 2; x x << 3;使用左移实现乘法运算仅限于乘以2的倍数 是不是只要左移就能够实现乘以2的倍数呢? char x 120; x x << 1;右移实现除法 右移n位等价于除…

2023 “华为杯” 中国研究生数学建模竞赛(E题)深度剖析|数学建模完整代码+建模过程全解全析

​ 问题一 血肿扩张风险相关因素探索建模 思路&#xff1a; 根据题目要求,首先需要判断每个患者是否发生了血肿扩张事件。根据定义,如果后续检查的血肿体积比首次检查增加≥6 mL或≥33%,则判断为发生了血肿扩张。 具体判断步骤: (1) 从表1中提取每个患者的入院首次影像检查…

数据库:Hive转Presto(一)

本人因为工作原因&#xff0c;经常使用hive以及presto&#xff0c;一般是编写hive完成工作&#xff0c;服务器原因&#xff0c;presto会跑的更快一些&#xff0c;所以工作的时候会使用presto验证结果&#xff0c;所以就要频繁hive转presto&#xff0c;为了方便&#xff0c;我用…

蓝牙核心规范(V5.4)10.5-BLE 入门笔记之HCI

HCI全称:HOST Constroller Interface 主机控制器接口(HCI)定义了一个标准化的接口,通过该接口,主机可以向控制器发出命令,并且控制器可以与主机进行通信。规范被分成几个部分,第一部分仅从功能的角度定义接口,不考虑具体的实现机制,而其他部分定义了在使用四种可能的…

记一次springboot的@RequestBody json值注入失败的问题(字段大小写的问题)

有时候做后端开发时&#xff0c;难免会与算法联调接口&#xff0c;很多算法的变量命名时全部大写&#xff0c;在实际springmvc开发中会遇到无法赋值的问题。 先粘贴问题代码 entity类 Data NoArgsConstructor EqualsAndHashCode(callSuper true) ToString(callSuper true) …

十,从摄像机打印立方体的一个外表面

从摄像机是与主摄像机保持同样的投影矩阵&#xff0c;所以&#xff0c;不用单独设置。如果把漫游器还是在&#xff08;1&#xff0c;0,0)这个位置&#xff0c;各个从摄像机看向上下左右前后六个面&#xff0c;那么会出现什么现象呢&#xff1f;应该是x正轴打印出来&#xff0c;…

LLaMa

文章目录 Problems403 代码文件LLaMA: Open and Efficient Foundation Language Models方法预训练数据结构优化器一些加速的方法 结果Common Sense ReasoningClosed-book Question AnsweringReading ComprehensionMassive Multitask Language Understanding Instruction Finetu…

【实验记录】AGW | Visible-Infrared Re-ID

【RT】Visible Thermal Re-IDDeep Learning for Person Re-identification: A Survey and Outlook中提出了一个针对单/跨模态行人重识别的baseline&#xff1a;AGW 做过两次&#xff0c;在测试阶段有问题&#xff0c;现在再重做一次&#x1f914;Code RTX3090 修改数据集路…

【空间-光谱联合注意网络:多时相遥感图像】

A Spatial–Spectral Joint Attention Network for Change Detection in Multispectral Imagery &#xff08;一种用于多光谱图像变化检测的空间-光谱联合注意网络&#xff09; 变化检测是通过比较双时相图像来确定和评估变化&#xff0c;这是遥感领域的一项具有挑战性的任务…

c++图像的边缘检测

图像的边缘检测 cv::Canny 是 OpenCV 中用于进行边缘检测的函数&#xff0c;特别是用于检测图像中的边缘。Canny 边缘检测是一种广泛使用的技术&#xff0c;它能够识别图像中的边缘&#xff0c;这些边缘通常表示对象之间的边界或图像中的显著特征 void cv::Canny(const cv::M…

【lesson7】git的介绍及使用

文章目录 什么是gitgit的历史git使用在gitee上创建仓库git clone HTTPS地址git add .git add 文件名git commit “日志”git pushgit loggit rm 文件名git statusgit pull 什么是git git是版本控制器&#xff0c;那么什么是版本控制器呢&#xff1f; 下面讲个故事为大家讲解一…

运算放大器(四):输入偏置电流

一、定义 运放输入级一般由 或 MOSFET 构成&#xff0c;理想情况下&#xff0c;运放的输入端没有电流流入。实际上为保证放大器工作在线性范围&#xff0c;运放的输入端一般设计成基极&#xff08;栅极&#xff09;开路&#xff0c;由外电路提供电流的方式&#xff0c;所以需要…

c++-string

文章目录 前言一、STL库介绍二、标准库中的string类1、string类介绍2、string类使用3.1 string类的构造函数3.2 string类对象的容量操作3.3 string类对象的遍历操作3.4 string类对象的访问操作3.5 string类对象的修改操作3.6 string类对象的字符串操作 三、模拟实现string类四、…

Prettier - Code formatter格式化规则文件

文章目录 前言安装使用 前言 先前公司在规范代码时,由于个人业务繁忙跟技术总监是后端出身用的IDEA不熟悉vsCode;以及大多数时都自己一个人负责一个项目,当时并不看重这些;最近在整理vue3tsvite的脚手架模板(平时工作用的react),开始整理格式化代码,方便之后 vue 和 react 中应…

element plus table 拖拽

element plus table 拖拽 sortablejs package.json "sortable.js": "^0.3.0","sortablejs": "^1.14.0", "vuedraggable": "^2.24.3",我的table 是在 el-dialog 里面的 在开发过程中出现过两个问题 1.进入加载 …

【力扣2154】将找到的值乘以 2

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述二、题目分析 一、题目描述 题目链接&#xff1a;将找到的值乘以 2 给你一个整数数组 nums &#xff0c;另给…

百度实习一面(知识图谱部门)

百度面经&#xff08;知识图谱部&#xff09;一面 1.自我介绍 介绍完了&#xff0c;打开共享&#xff0c;对着简历一点一点问 2.ffmpeg在项目中是怎么使用的 回答了ffmpeg在项目中使用的命令&#xff0c;用来干了什么 3.为什么使用toml配置&#xff0c;了解过yml配置吗&am…

Mock.js之Element-ui搭建首页导航与左侧菜单

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《springMvc使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 1、Mock.js的使用 1.1.什么是Mock.js Mock.js是一个模拟数据的生成器&#xff0c;用来帮助前…