您的位置:首页 > 编程语言 > Java开发

LeetCode - Single Number II

2014-02-28 01:15 351 查看

题注

这开学以后,论文阅读和撰写的压力就慢慢上来了。确实,既能够保证编程水平不下降(或者提升),又能够保证理论上的深入性,是一件非常困难的事情啊。我的偶像Dan Boneh,实际上是一个完全不会代码的人… 他的一个准学生Brent Waters也是不懂代码(Waters好多论文的运行时间是从理论时间估计出来的,或者是别人帮他实现的)。Graig Gentry算是Boneh学生里面理论最厉害的了,论文一大堆!但是他也是一点代码都不懂…

我短期的理想是将把在论文中的密码学应用到实际中,所以两手抓两手都要硬,真是困难啊… 以后争取每天回家花点时间哪怕做一道LeetCode的题呢,虽然工程类的编程能力肯定会下降(因为OO的概念快被磨没了嘛,而且一些库函数也快不会用了…),但是代码本身的感觉还在,而且提高算法能力也对看论文有好处吧!

题目

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?

分析和代码

解法一:HashMap

这道题,一种非常直观的办法是用Hash Map或者Map来做。Map中的Key用数组中各个元素值来表示,Map中的Value表示每个数字出现的次数。也就是说,我们构造了一个O(n)空间复杂度的计数器,分别记录每一个元素出现的次数,最后用一次O(n)的检索,来看计数器中哪一个是1即可。

这种方法很直观,时间复杂度和空间复杂度都为O(n),但是这是一个通用解法,可以应用在所有这类问题中。举个例子:如果题目变为,给定一个整数数组,每一个元素出现了m次,只有一个元素出现过n次,找到这一个元素。那么这种Map的方法也可以使用。因此,我个人认为在实际应用中,我们应该考虑这种通用的解决方法,虽然效率不会达到最高,但是很具有代表性。

解法一代码

public class Solution {
public int singleNumber(int[] A) {
int result;
//check the validity of input array
if(A.length < 1 || A.length % 3 != 1){
return -1;
}
HashMap<Integer,Integer> countMap = new HashMap<Integer, Integer>();
for (int i = 0; i < A.length; i++){
// if countMap does not contain value with key i, add a new element
if (!countMap.containsKey(A[i])){
countMap.put(A[i], 1);
} else {
countMap.put(A[i], countMap.get(A[i]) + 1);
}
}

//finding values with count number equals 1
Iterator<Map.Entry<Integer, Integer>> iterator = countMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = iterator.next();
if (entry.getValue() != 3) {
result = entry.getKey();
return result;
}
}
return -1;
}
}

解法二:按位记录

既然我们可以构造一个O(n)长的计数器,我们能不能取而代之,弄一个O(1)长的计数器呢?当然可以。思路是:我们把每一个数看成二进制的,分别考虑二进制数的每一位。假定我们现在考虑第i位,如果一个数出现过3次,那么无论这个数第i位为0还是为1,出现0和1的次数都为3;但是,如果一个数只出现过1次,且这个数第i位为1,那么1只能出现1次。因此,我们分别考察每一位数中,1出现的次数是否能被3整除。

(1)如果不能整除,那么只出现1次的这个数第i位必定为1。

(2)如果能整除,那么只出现1次的这个数第i位必定为0。

这样,我们循环32次(因为Java中int的长度为32位),按位计算1出现的次数并除以3取余数,并把这32个结果按照高低位拼接起来,拼接的结果就是正确的结果了。由于我们只用到3个int临时变量,以及需要1个result返回结果,其空间复杂度为O(1)。时间复杂度不变,为O(n)。实际上,因为我们对每一位都记了一次数,因此其准确的时间复杂度为32n。

解法二代码

public class Solution {
public int singleNumber(int[] A){
int i, j, bitCount;
int result = 0;

for (i = 0; i < 32; i++) {
bitCount = 0;

// add every i-th bit of the integers into bitCount
for (j = 0; j < A.length; j++) {
bitCount += (A[j] >> i) & 1;
}
bitCount = bitCount % 3;
// switch to the right position and add it to the result
result += bitCount << i;
}
return result;
}
}

解法二的通用解法

解法二是不是一个通用解法呢?如果题目变为,给定一个整数数组,每一个元素出现了m次,只有一个元素出现过1次,找到这一个元素。我们可以用类似的方法做,只不过计算1出现的次数除以m取余数即可。如果只有一个元素出现过n次,那么,我们先判断每一位的计数器是否能够整除m,如果不能,那么那个特殊的数的这一位一定为1(只有特殊数这一位为1才会导致结果不能整除m)。因此,这是一个通用的解法。

通用解法代码

public class Solution {
/**
* Given an array of integers, every element appears m times except for one that appears n times.
* Find that single one.
* @param A array of integers
* @param m every element except one appears m times
* @param n special one appears n times
* @return the special integer
*/
public int singleNumber(int[] A, int m, int n){
int i, j, bitCount;
int result = 0;

for (i = 0; i < 32; i++) {
bitCount = 0;

// add every i-th bit of the integers into bitCount
for (j = 0; j < A.length; j++) {
bitCount += (A[j] >> i) & 1;
}
if (bitCount % m != 0){
bitCount = 1;
} else {
bitCount = 0;
}
// switch to the right position and add it to the result
result += bitCount << i;
}
return result;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Algorithm Java leetcode