Bit Manipulation —— LeetCode题解Single-number系列

2023-10-19 20:10

本文主要是介绍Bit Manipulation —— LeetCode题解Single-number系列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Bit Manipulation需要把每一个变量都想象成一串二进制编码,用and,
or, not等逻辑进行运算。在single-number系列的题中,需要把int想象成一串32位的二进制编码。所以所这种题要先转换成二进制的思维。解法的确需要“取巧”,如果没有见过此类题的话还挺难想出来的。

Single-number

Given an array of integers, every element appears twice except for
one. Find that single one.
Note: Your algorithm should have a linear
runtime complexity. Could you implement it without using extra memory?

这里重点需要掌握异或运算(XOR)

  • 每个数XOR 0就是它本身
  • 每个数XOR它本身是0
  • XOR符合交换律

将给定的数两两进行异或运算,由于满足交换律,所以A^B^C^B^A =
A^A^B^B^C,也就是说可以看成所有出现两次的数XOR自身变成0,最后再用0 XOR那个出现一次的数,得出的结果就是出现一次的数。

想通原理之后代码超简单,一个loop就搞定了。

public class Solution {public int singleNumber(int[] A) {int res = 0;for(int i:A){res = res^i;}return res;}
}

另一种角度:这使我忽然想到了最强大脑的层叠消融。把每个int想象成一个set,就是在32位里1出现的位置的集合。那么每两个int代表的set可能有重叠或者没有。然后在两个int XOR的时候,这两个set都没有覆盖或者都覆盖的地方因为“奇消偶不消”的原则,清零了,只被一个set覆盖的地方会保留。那么把给定的数两两XOR之后,那些出现两次的数一定会给某些区域贡献两次,所以就抵消了。最后依然保留下来的位置,是得到了奇数次的贡献,于是这些位置就是answer的二进制编码中 1出现的位置。

Single-number (ii)

Given an array of integers, every element
appears three times except for one. Find that single one.
Note: Your algorithm should have a linear
runtime complexity. Could you implement it without using extra memory?

上一题当中XOR自动帮我们实现了“同一位置得到两次贡献就清零”的功能。现在这个题需要手动实现“同一位置得到三次贡献就清零”。我们需要维持三个set (其实就是三个int),分别代表哪些位置上出现过一次1,出现过两次1,和出现过三次1。出现到四次就自动回到一次那个set。用到的依旧是逻辑运算。

这三个set的名称分别是:one, two, three。A代表每一轮迭代新看到的int
bit-manipulation1

每一次迭代进入到新的two里面的是 A&One 以及 Two里面排除(A&Two) 的部分。Two里面排除A&Two用逻辑运算来写就是 Two^(A&Two)

bit-manipulation2

Similarly,一次迭代进入到新的three里面的是 A&Two 以及 Three里面排除(A&Three) 的部分( (A&Three)^Three ).

bit-manipulation3

每一次迭代进入到新的one里面的多了一项,包括 A&Three , One里面排除(A&One) 的部分,还有 A里面排除one&A, two&A, three&A 的部分。注意,由于one, two, three彼此之间没有重叠,所以“A里面排除one&A, two&A, three&A” 就可以用A^(A&one)^(A&two)^(A&three)表示。

逻辑通了之后代码依旧超简单,只要每一轮根据A和上一轮的one, two, three 得到新的one, two, three,进行update即可。全部迭代完之后one这个set就代表那个只出现一次的int的二进制表示中1出现的位置。(最后的one就是answer)

注意:boolean之间的运算是 | |,&&这样,但int之间做bit
manipulation就是^, |, &,都是单个。

public class Solution {public int singleNumber(int[] A) {int one = 0;int two = 0;int three = 0;for(int i:A){int new_one = (i & three) | one^(i & one) | i^(i&one)^(i&two)^(i&three);int new_two = (i&one) | two^(i&two);int new_three = (i&two) | three^(i&three);one = new_one;two = new_two;three = new_three;}return one;}
}

Single-number (iii)

(牛客上面没有这道题,是leetcode官网找到的,难度比前两道题又有所提升) Given an array of numbers nums, in which exactly two elements
appear only once and all the other elements appear exactly twice. Find the two
elements that appear only once.
For example, Input: [1,2,1,3,2,5]; Output:[3,5]
Note:

  1. The order of the result is not
    important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear
    runtime complexity. Could you implement it using only constant space
    complexity?

思路:先把所有的数都XOR一遍,得到的是只出现一次的那两个数不重叠的部分
(也就是answer_1 ^ answer_2)。我们希望按照某个规矩把所有的数分组,并且要保证,1)同样的数要分到同一组,2)只出现一次的两个数需要分到不同的组。这样对两个组分别进行single-number的算法就可以找出它们俩。

那么该怎样分组呢?
single-number3

如图,把所有数字都XOR一遍之后,得到的是上图带颜色的部分。然后我们希望从浅黄色部分(桔黄色的也行,这俩顺序没有分别)找到一个位置,只有answer_2在这个位置上的bit是1,answer_1在这个位置上的bit是0。

于是,我们可以把在这个位置上bit是1的数分到第一组,在这个位置上bit是2的数分到第二组。这种分组方式可以满足上述的两个要求。

class Solution {public int[] singleNumber(int[] nums) {int temp = 0;for(int i:nums){temp = temp^i;}/*int one = 1; //find a bit s.t. temp has ‘1’ on that bit. For initialization,the smallest bit is set to 1.while ((one&temp)!=one){one <<= 1; // 如果temp在这一位上不是1,就shift to left一位}*/int one = temp&(-temp);// 从大神那里看到a&(-a)就是从最小位开始算第一个是1的位// 因为a算负数是先将二进制取反然后+1.// 开始分组boolean[] group = new boolean[nums.length];for(int i=0; i<nums.length; ++i){if ((nums[i]&one) == (temp&one)){group[i] = true;}else {group[i] = false;}}//把每个组里的数互相XOR一遍int answer_1 = 0;int answer_2 = 0;for(int i=0; i<nums.length; ++i){if(group[i]) {answer_1 = answer_1^nums[i];}else {answer_2 = answer_2^nums[i];}}int[] answer = new int[2];answer[0] = answer_1;answer[1] = answer_2;return answer;}
}

参考链接:https://www.cnblogs.com/grandyang/p/4741122.html

这篇关于Bit Manipulation —— LeetCode题解Single-number系列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/242109

相关文章

Linux使用粘滞位 (t-bit)共享文件的方法教程

《Linux使用粘滞位(t-bit)共享文件的方法教程》在Linux系统中,共享文件是日常管理和协作中的常见任务,而粘滞位(StickyBit或t-bit)是实现共享目录安全性的重要工具之一,本文将... 目录文件共享的常见场景基础概念linux 文件权限粘滞位 (Sticky Bit)设置共享目录并配置粘

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

题目1380:lucky number

题目1380:lucky number 时间限制:3 秒 内存限制:3 兆 特殊判题:否 提交:2839 解决:300 题目描述: 每个人有自己的lucky number,小A也一样。不过他的lucky number定义不一样。他认为一个序列中某些数出现的次数为n的话,都是他的lucky number。但是,现在这个序列很大,他无法快速找到所有lucky number。既然