从零学算法(LCR 178)

news2024/11/25 11:54:27

教学过程中,教练示范一次,学员跟做三次。该过程被混乱剪辑后,记录于数组 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 最终计算出的对应的 新one
      if two==0:
      	if n==0: #记得这个 n 表示的是在 32 位的某一位上的数,因为 one two 也是用来表示某一位相加过程中的状态
      		# 你也可以自己划分一下 if one== 0,if one == 1,会发现最后结果都是 one
      		one = one
      	if n==1:
      		one = ~one # one 的相反数
      if two==1:
      	# 此时 two one 只可能为 10,那么你 n 为 0 ,one 还是保持为 0,n 为 1,我就得变成 00,one 还是 0
      	# 也就是说 n 首先此时必定为 0,并且无论变或不变最后都成了 0
      	one = 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^n
      if 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 = two
      		elif one == 1:
      			two = two
      	if n==1:
      		if one == 0:
      			two = 1 #~two
      		if one == 1:
      			two = two
      elif two==1:
      	if n==0:
      		if one == 0:
      			two = two
      	if n==1:
      		if one == 0:
      			two = 0 #~two
    
  • 简化一下,结果还是为 two 的情况可以省略:
  •   if two==0:
      	if n==1:
      		if one == 0:
      			two = 1
      elif two==1:
      	if n==1:
      		two = 0
    
  • 继续简化:
  •   if two==0:
      	if n==1 && one == 0:
      		two = 1
      elif two==1:
      	if n==1 && one == 0:
      		two = 0
    
  • 再简化,为了使得 n==1 && one == 0 为 true,你会发现可以转换成 n&~one
  •   if two==0:
      	if n&~one:
      		two = 1
      elif two==1:
      	if n&~one:
      		two = 0
    
  • 到了这里你不觉得有点眼熟吗,你可以扩充完整来列出真值表
  •   if two==0:
      	if n&~one:
      		two = 1 # 这个 two 就是 newtwo
      	else:
      		two = 0
      elif two==1:
      	if n&~one:
      		two = 0
      	else:
      		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 而不是 one
          int 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.coloradmin.cn/o/1043049.html

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

相关文章

java中使用redis2个库并支持Redis哈希表

一个redis实例&#xff0c;默认包含16个库&#xff0c;序号从0到15。在redis命令行中&#xff0c;可以用select 序号来切换。我最近在做的一个项目中&#xff0c;需要使用redis的2个库。一个是由其他子系统写入&#xff0c;web后端&#xff08;java&#xff09;只读取&#xff…

瑞芯微RK3568|SDK开发之Kernel编译

1. Kernel手动编译 1.1 kernel查询帮助 使用./build.sh -h kernel查看kernel的详细编译命令如下所示。 图1.1编译内核 上图表示&#xff0c;单独编译kernel固件分为三步&#xff0c;进入kernel目录&#xff0c;选择默认配置文件&#xff0c;编译镜像。 1.2 kernel…

多家快递公司物流信息批量查询方法及操作说明

在网购时代&#xff0c;快递单号的查询和管理成了一个重要的问题。尤其是对于需要处理大量快递单号的人来说&#xff0c;一个高效、便捷的查询工具至关重要。本文将介绍如何使用“固乔快递查询助手”软件&#xff0c;快速、准确地查询多家不同快递公司的快递单号&#xff0c;并…

最新白皮书:软件定义的硬件打开通往高性能数据加速的大门

在众多行业的数字化转型过程中&#xff0c;基于硬件的数据处理加速是构建高性能、高效率智能系统的关键之处&#xff0c;因而市场上出现了诸如FPGA、GPU和xPU等许多通用或者面向特定应用&#xff08;如NPU&#xff09;的硬件加速器。尽管它们的性能和效率都高于通用处理器&…

基于R的linkET包qcorrplot可视化Mantel test相关性网络热图分析correlation heatmap

写在前面 需求是对瘤胃宏基因组结果鉴定到的差异菌株与表观指标、瘤胃代谢组、血清代谢组、牛奶代谢组中有差异的部分进行关联分析&#xff0c;效果图如下&#xff1a; 数据准备 逗号分隔的csv格式文件&#xff0c;两个表格&#xff0c;一个是每个样本对应的表观指标数据&…

工业交换机常见的故障有哪些?

通常情况下&#xff0c;工业交换机出现故障可以分为两类&#xff1a;软件性能故障和硬件物理故障。软性能故障通常指工业交换机在研发设计阶段出现的问题。 物理层故障主要指交换机本身的硬件故障以及连接交换机的物理线路故障。安防专用工业交换机的交换是根据通信双方传输信…

构建智能客服知识库,优化客户体验不是难题!

在当今快节奏的商业环境中&#xff0c;客户都希望得到及时个性化的支持&#xff0c;拥有一个智能客服知识库对于现代企业至关重要。智能客服知识库是一个集中存储、组织和访问与客户服务互动相关的信息的综合性知识库。它为企业提供了全面的知识来源&#xff0c;使他们能够为客…

PHP循环获取Excel表头字母A-Z,当超过时输出AA,AB,AC,AD······

PHP循环获取Excel表头字母A-Z&#xff0c;当超过时输出AA,AB,AC,AD PHP循环生成Excel的列字母表 $count_num 26 * 27; $letter A; $arr []; while($count_num--){$arr[] $letter;$letter; }结果如下&#xff1a; 转为JSON更为直观&#xff1a; ["A","B&…

MySQL 8 0 填坑历险之安装与基本用户权限

MySQL 8.0 填坑历险之安装 MySQL 是当下流行的关系型数据库&#xff0c;搜掠了各种文章&#xff0c;多少有些偏差&#xff0c;为此来分享下我的经历&#xff1b; 安装环境 环境版本备注Ubuntu 18.0418.04.6 LTS (Bionic Beaver)LinuxKernel5.4.0-42-genericMySQL 8.0.23mysq…

上海亚商投顾:沪指震荡调整 减肥药、华为概念股持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日震荡调整&#xff0c;上证50午后一度跌近1%&#xff0c;券商、保险等权重板块走势低迷。 华为概…

大咖共探AGI时代机遇,腾讯云助力大模型规模化应用提速

引言 2023 年&#xff0c;科技圈的“顶流”莫过于大模型。自 ChatGPT 的问世拉开大模型与生成式 AI 产业的发展序幕后&#xff0c;国内大模型快速跟进&#xff0c;已完成从技术到产品、再到商业的阶段跨越&#xff0c;并深入垂直行业领域。 新技术的爆发&#xff0c;催生新的应…

唤醒手腕 2023年 B 站课程 Golang 语言详细教程笔记(更新中)

0001、1000集GO语言Flag毒誓 唤醒手腕UP猪Pig目标花费1000集进行讲解Go语言视频学习教程&#xff08;有趣的灵魂&#xff0c;适合小白&#xff0c;不适合巨佬&#xff09;&#xff0c;从2023年3月19日开始&#xff0c;将会一直每天更新&#xff0c;准备在2024年5月1日之前更新…

(主)9.26锁存器状态机方法、题解大综合(加码加码加码)

触发器 D D锁存器就是在有时钟信号时&#xff0c;D信号是啥&#xff0c;Q就是啥&#xff0c;并且在时钟信号消失时&#xff0c;锁存住消失前一刻的信号状态 主从D锁存器中&#xff0c;D信号是主锁存器的输入信号&#xff0c;主锁存器的信号是从锁存器的输入信号 用D实现的电…

苹果电脑专业的条形码工具iBarcoder:助力高效条形码生成,提升工作效率

iBarcoder for mac是Mac os平台上的一款优秀条码生成软件。它可以帮助用户建立不同类型的条形码&#xff0c;并允许用户设计并打印自己的条码标签&#xff0c;创建条码图形的零售包装、书籍、贴纸等。 在iBarcoder中&#xff0c;用户可以方便地创建专业的条形码标签&#xff0…

Linux服务器安装Anaconda 配置远程jupyter lab使用虚拟环境

参考的博客&#xff1a; Linux服务器安装Anaconda 并配置远程jupyter lab anaconda配置远程访问jupyter&#xff0c;并创建虚拟环境 理解和创建&#xff1a;Anaconda、Jupyterlab、虚拟环境、Kernel 下边是正文了。 https://www.anaconda.com/download是官网网址&#xff0c;可…

C/C++进程线程超详细详解

目录 前言 一、进程基础 1.进程概念 2.进程特征 3.进程状态&#xff08;如图清晰可见&#xff09; 4&#xff0c;进程的标识 实例代码如下&#xff1a; 5.进程的种类 实例shell脚本程序如下: 二、进程API 1.创建子进程 实例代码如下&#xff1a; 2.exec函数族 函数…

ptmalloc源码分析 - free()函数的实现(10)

目录 一、步骤1-标记MMAP分配的使用munmap_chunk释放 二、步骤2-释放的是小块内存&#xff0c;则chunk放入fastbin 三、步骤3-如果不是MMAP分配&#xff0c;则释放到unsorted bins 四、步骤4-如果nextchunk 就是Top chunk&#xff0c;则直接扩容Top chunk 五、步骤5-如果释…

分享从零开始学习网络设备配置--任务4.1 IPv6地址的基本配置

任务描述 某公司构建了互联互通的办公网&#xff0c;需要不断扩大网络规模。网络管理员小赵决定采用IPv6的地址&#xff0c;满足公司网络规模的未来发展。 由于IPv4地址耗尽及IPv4地址区域分配不均衡&#xff0c;成为运营商必须面临的一个问题。另外随着互联网的商业化&#…

【python】将python脚本打包成可执行的.exe文件 推荐使用auto-py-to-exe

将python脚本打包成可执行的exe文件 1.不推荐使用网上流行的打包程序&#xff0c;主要是有以下几点&#xff0c;一、cx_freeze与Pyinstaller都是命令行的形 式&#xff0c;且对于Pyinstaller的打包常常需要打包完成后&#xff0c;再修改打包后的原路径&#xff0c;二、非可视…

vue wangEditor富文本编辑器 默认显示与自定义工具栏配置

1.vue 显示wangEditor富文本编辑器 <template><div style"border: 1px solid #ccc;"><Toolbar style"border-bottom: 1px solid #ccc" :editor"editor" :defaultConfig"toolbarConfig" :mode"mode"/><…