LintCode 83. Single Number II (Medium)

LeetCode 137. Single Number II (Medium)

以下算法的复杂度都是:

时间复杂度: O(n)

空间复杂度: O(1)

解法1. 32个计数器

最简单的思路是用32个计数器, 满3复位0.

class Solution {
public:
int singleNumberII(vector<int> &A) {
int cnt[32] = {0};
int res = 0;
for (int i = 0; i < 32; ++i) {
for (int n : A) {
cnt[i] += (n >> i) & 1;
cnt[i] %= 3;
}
res |= cnt[i] << i;
}
return res;
}
};

解法2. 找规律

我的思路是, 解法1中的计数其实只需要两个bit就够了. 所有的首个bit记做res, 所有的第二个bit记做carry, 找规律:

如果A[i]的第k位是0, 则rescarry的第k位保持原样.

如果A[i]的第k位是1, 则:

res carry res' carry'
0 0 1 0
1 0 0 1
0 1 0 0

此时(A[i][k]=1时)的规律就是:

res'[k]=~res[k] & ~carry[k]

carry'[k]=res[k] & ~carry[k]

class Solution {
public:
int singleNumberII(vector<int> &A) {
int res = 0, carry = 0;
for (int n : A) {
for (int i = 0; i < 32; ++i) {
int mask = (1 << i);
int bit = n & mask;
if (bit) {
int newRes = (~mask & res) + ((~res & mask) & (~carry & mask));
carry = (~mask & carry) + ((res & mask) & (~carry & mask));
res = newRes;
}
}
}
return res;
}
};

解法2.1. 解法2的简化

解法2中, 将A[i][k]=0=1的情况合并, 可以得到:

res'[k]=(A[i][k] & ~res[k] & ~carry[k]) | (~A[i][k] & res[k])

carry'[k]=(A[i][k] & res[k] & ~carry[k]) | (~A[i][k] & carry[k])

这样的好处是可以32位一起算, 而不用一位一位地算:

res'=(A[i] & ~res & ~carry) | (~A[i][k] & res)

carry'=(A[i] & res & ~carry) | (~A[i][k] & carry)

class Solution {
public:
int singleNumberII(vector<int> &A) {
int res = 0, carry = 0;
for (int n : A) {
int newRes = (n & (~res & ~carry)) | (~n & res);
carry = (n & (res & ~carry)) | (~n & carry);
res = newRes;
}
return res;
}
};

解法3. one, two, three

Discuss中看到的解法, 自己实在想不出来. 用one, twothree三个int值作为bit flags.

循环对A[0]A[n]进行考察, 当考察A[i]时:

S[i]={A[0],...,A[i]},

S[i]中所有数字的第k位bit的数目%3==1, 则one的第k位为1, 否则为0.

S[i]中所有数字的第k位bit的数目%3==2, 则two的第k位为1, 否则为0.

three是一个临时变量, 用于记录这一轮中, 哪些bit的数目恰巧是3的倍数.

two |= one & n;: 给two加上那些从1到2的数字.

one ^= n;: 这句比较巧妙, 既删掉了会变成2的那些1, 又加上了新的1.

three = one & two;: 1+2=3...

one&= ~three: 从1中刨去那些成为3的1.

two&= ~three: 从2中跑去那些成为3的2.

...如果你能解释得更清晰易懂, 欢迎留言!

class Solution {
public:
int singleNumberII(vector<int> &A) {
int one = 0, two = 0, three = 0;
for (int n : A) {
two |= one & n;
one ^= n;
three = one & two;
one &= ~three;
two &= ~three;
}
return one;
}
};

解法3.1. 解法3的变形

自己没想出解法4, 但是参照它的思路, 写了一个对自己比较直观的算法.

three = two & n;: 算出从2变成3的那些2.

two = (two & ~three) | (one & n);: (two & ~three)是从2中刨去那些变为3的2, (one & n)是加上那些从1变成2的1.

one = (one & ~n) | (n & ~three & ~two);: (one & ~n)是从1中刨去那些变成2的1, (n & ~three & ~two)是加上那些没给"2变3"或"1变2"用过的1.

class Solution {
public:
int singleNumberII(vector<int> &A) {
int one = 0, two = 0;
for (int n : A) {
int three = two & n;
two = (two & ~three) | (one & n);
one = (one & ~n) | (n & ~three & ~two);
}
return one;
}
};

[OJ] Single Number II的更多相关文章

  1. 【leetcode】Single Number && Single Number II(ORZ 位运算)

    题目描述: Single Number Given an array of integers, every element appears twice except for one. Find tha ...

  2. 【题解】【位操作】【Leetcode】Single Number II

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

  3. Single Number,Single Number II

    Single Number Total Accepted: 103745 Total Submissions: 218647 Difficulty: Medium Given an array of ...

  4. leetcode 之 Single Number II

    问题来源:Single Number II 问题描述:给定一个整数数组,除了一个整数出现一次之外,其余的每一个整数均出现三次,请找出这个出现一次的整数. 大家可能很熟悉另一个题目(Single Num ...

  5. 【leetcode78】Single Number II

    题目描述: 给定一个数组,里面除了一个数字,其他的都出现三次.求出这个数字 原文描述: Given an array of integers, every element appears three ...

  6. leetcode 136. Single Number 、 137. Single Number II 、 260. Single Number III(剑指offer40 数组中只出现一次的数字)

    136. Single Number 除了一个数字,其他数字都出现了两遍. 用亦或解决,亦或的特点:1.相同的数结果为0,不同的数结果为1 2.与自己亦或为0,与0亦或为原来的数 class Solu ...

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

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

  8. 【LeetCode】137. Single Number II (3 solutions)

    Single Number II Given an array of integers, every element appears threetimes except for one. Find t ...

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

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

随机推荐

  1. HW-文件恢复-测试300

    //package t0817; import java.util.*; public class FileRelive {//类名换成Main public static void main(Str ...

  2. union 和 union all 的区别

    Union因为要进行重复值扫描,所以效率低.如果合并没有刻意要删除重复行,那么就使用Union All  两个要联合的SQL语句 字段个数必须一样,而且字段类型要“相容”(一致): 如果我们需要将两个 ...

  3. ugui自制摇杆。

    珍爱生命,远离插件. 以上8个字,好好理解. 反正我是这么觉得. 我说的是unity,不是魔兽世界. 总有一天,我会一句一句写出属于自己的东西. 可以开始主题了.         如图所示,建立一个画 ...

  4. Java实战之02Hibernate-02映射、一级缓存、实体对象状态

    五.映射基础 1.实体类采用javabean的编写规范 JavaBean编写规范: a.类一般是public的 b.有默认的构造方法 c.字段都是私有的 d.提供公有的getter和setter方法 ...

  5. WPF 程序中启动和关闭外部.exe程序

    当需要在WPF程序启动时,启动另一外部程序(.exe程序)时,可以按照下面的例子来: C#后台代码如下: using System; using System.Collections.Generic; ...

  6. cocos2dx 初探 - VS2012 HELLOWORLD

    最近决定用cocos2dx 来做些试验性的东西,先装了个vs2012 再从网上下了cocos2dx-2.1.4就开工了. 仅是Windows 桌面调试还是很简单的. 上面三个项目源: Hellocpp ...

  7. hdu 5014 Number Sequence

    为了a异或b的和最大,只需另b在不大于n的情况下按位取反即可. 这里有两个输出小技巧可以参考: 1.在用printf输出__int64时,在windows下使用格式"%I64d", ...

  8. resid入门笔记(二)

    本节介绍redis 消息订阅   密码  持久化  主从配置   首先我对消息订阅理解的不深,应该说仅知道概念吧 发送消息  cctv1 发送 hello   cctv2 发送 ‘你好’ client ...

  9. Linux C 程序 基础(FOUR)

    1.标识符:C语言本身不限制变量长度,但是某些编译器会限制变量长度,命名最好不要超过8位.         以数字开头,保留字,*,空格非法 2.关键字:类型说明符,int , 语句定义符,if el ...

  10. BrowserSync:跨浏览器实时同步预览

    BrowserSync:跨浏览器实时同步预览 2016.09.11 官方网站:https://www.browsersync.io/ 项目仓库:https://github.com/Browsersy ...