Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

题意: 在一个整型数组里,只有一个数字出现一次,其他数字都出现了3次,求这个只出现一次的数字(single number)。

这真是非常非常有意思的一道题目。如果直接统计各数字出现的次数当然也能达到目的,不过空间复杂度为O(N)。能否用O(1)空间解决此问题呢?

首先注意到,对于某个二进制位,如果single number的该位为1,那么所有数字在该二进制位上1出现的总次数不能被3 整除,且余数为1。比如数组{3, 3, 3, 2},转化为二进制表示: {11, 11, 11, 10},二进制位01出现的次数为3次,二进制位10出现的次数为4次。

因此我们只需要计算出每个二进制位1出现次数除以3的余数,就可以轻松得到该single number。

模3的余数有3个: 0, 1, 2,对应二进制表示: 00, 01, 10,需要两个二进制位来表示。

我们可以用两个整数one和two来分别表示各二进制位1出现的次数模3的余数。

显然直接累加统计次数然后取模是不现实的,这时候就要用到位运算了,这也是本题解法最有技巧的一部分。

如何设计位运算模拟累加模3的结果呢?

假设当前累加到数组中的数字num,考察one,two和num在二进制位取不同值下,累加1的出现次数模3的结果:

One'和Two'分别表示累加后模3的新余数的第一位和第二位。

举例

假设当前二进制位1出现的次数模3为1,数字num在该二进制位为0,那么1的出现次数不变,模3依然为1,对应两个真值表的第二行: One' = 1, Two' = 0。

假设当前二进制位1出现的次数模3为2,数字num在该二进制位为1,那么1的出现次数+1,模3结果变为0,对应两个真值表的第四行: One' = 0, Two' = 0。

因此,以上真值表就包含了所有情况下累加模3的结果。

如何将真值表转化为位运算的逻辑表达式呢?这时候就要用到一个大杀器:卡诺图 (Karnaugh map)

以下是两个真值表转化来的卡诺图:

One'

Two'

解释:

列头表示num在该位为0或1的情况,行头表示当前余数的值。

两张表表内的0和1分别表示给定num和余数,进行累加模3操作后,One' 和 Two'的值,大家可以对比之前的真值表体会一下。

注意余数为11的情况是不存在的,因此结果标记为x,这列其实可以删掉。之所以写出来是为了体现卡诺图的逻辑条件是按格雷码排列的,简单说就是相邻的二进制条件只有1位是不一样的。这样方便转化为逻辑表达式的时候进行化简,简单说就是卡诺图里相邻并且构成矩形的1的情况可以简化为用一个逻辑表达式来表示。具体可以参考卡诺图Wiki页面的例子。这个问题的卡诺图比较简单,不存在化简的情况。

接下来将卡诺图转换为逻辑表达式。

以One'为例。图中只有两个格子为1,也即num = 0, two = 0, one =1 和 num = 1, two = 0, one = 0的情况。因此我们有:

one = (~num & ~two & one) + (num & ~one & ~two);

只有以上两种情况之一,one可以取值1,其他情况皆为0。注意C++里位操作符&, ^, |等的优先级低于加法操作符,因此需要括号括起来。

Two的逻辑表达式可以类推。

以下为完整代码。因为single number的1只出现一次,模3余数肯定为1,因此直接返回one即可。

int singleNumber(vector<int>& nums) {
int one = , two = ;
for (int i = ; i < nums.size(); i++) {
int num = nums[i];
int newOne = (~num & ~two & one) + (num & ~one & ~two);
two = (~num & two & ~one) + (num & ~two & one);
one = newOne;
} return one;
}

详解LeetCode 137. Single Number II的更多相关文章

  1. LeetCode 137. Single Number II(只出现一次的数字 II)

    LeetCode 137. Single Number II(只出现一次的数字 II)

  2. Leetcode 137 Single Number II 仅出现一次的数字

    原题地址https://leetcode.com/problems/single-number-ii/ 题目描述Given an array of integers, every element ap ...

  3. LeetCode 137 Single Number II(仅仅出现一次的数字 II)(*)

    翻译 给定一个整型数组,除了某个元素外其余的均出现了三次. 找出这个元素. 备注: 你的算法应该是线性时间复杂度. 你能够不用额外的空间来实现它吗? 原文 Given an array of inte ...

  4. [LeetCode] 137. Single Number II 单独数 II

    Given a non-empty array of integers, every element appears three times except for one, which appears ...

  5. [LeetCode] 137. Single Number II 单独的数字之二

    Given a non-empty array of integers, every element appears three times except for one, which appears ...

  6. leetcode 137. Single Number II ----- java

    Given an array of integers, every element appears three times except for one. Find that single one. ...

  7. Java [Leetcode 137]Single Number II

    题目描述: Given an array of integers, every element appears three times except for one. Find that single ...

  8. LeetCode 137 Single Number II 数组中除了一个数外,其他的数都出现了三次,找出这个只出现一次的数

    Given an array of integers, every element appears three times except for one, which appears exactly ...

  9. Java for LeetCode 137 Single Number II

    Given an array of integers, every element appears three times except for one. Find that single one. ...

随机推荐

  1. apt 安装 tomcat

    apt 安装 tomcat 直接使用 agt-get 安装 apt-get install tomcat7 # or apt-get install tomcat8 需要一段时间后就安装完成了. 安装 ...

  2. 微软报表A4纸大小规则

    总页宽:21cm,总页高:29.7cm 上下左右边距均为2.5cm 页眉页脚均为0.75cm 正文内容宽:16cm,高23.2cm

  3. 做 fzu oj 1003 简单的枚举

    暴力求解法---简单枚举 定义一个函数(函数的内容大概是包含了题目所给的限制条件),然后主函数就是通过循环进行枚举,枚举出可能的元素,带入函数中进行验证,如果符合函数所给的情况,则为其解.

  4. 高可用web框架

    nginx nginx简介 Nginx是一个自由.开源.高性能及轻量级的HTTP服务器及反转代理服务器.Nginx以其高性能.稳定.功能丰富.配置简单及占用系统资源少而著称. Nginx 超越 Apa ...

  5. Java的Stream流

    yi.控制台输入输出流, 1.读取控制台输入 Java的控制台输入由System.in完成.为了获得一个绑定到控制台的字符流,可以把System.in包装在一个BufferedReader对象中来创建 ...

  6. JavaScript设计模式之设计原则

    何为设计 即按照哪一种思路或者标准来实现功能,功能相同,可以有不同的设计方案来实现 伴随着需求的增加,设计的作用就会体现出来,一般的APP每天都在变化,更新很快,需求不断在增加,如果设计的不好,后面很 ...

  7. 【[SDOI2008]Sandy的卡片】

    被\(mhr\)的暴力干翻了 这道题做法还是非常好想的 先做一遍差分,在每个串的某尾插入一个特殊字符,再将所有的串拼接在一起 现在的问题就转化为找到一个最长的公共子串使得其出现了\(n\)次,但是在一 ...

  8. [SQL注入] 逗号拦截绕过

    在SQL注入时,在确定了注入点后,一般都需要使用联合查询猜表的列数,也就是常见的order by n,n从大到小,直到返回正常,就确定了当前查询的列的个数. 然后再使用 UNION SELECT 1, ...

  9. 通讯协议(一)HTTP协议

    协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器.目前我们使 ...

  10. Android开发之自己定义TabHost文字及背景(源码分享)

    使用TabHost 能够在一个屏幕间进行不同版面的切换,而系统自带的tabhost界面较为朴素,我们应该怎样进行自己定义改动优化呢 MainActivity的源码 package com.dream. ...