LeetCode - Single Number II

题注

这开学以后,论文阅读和撰写的压力就慢慢上来了。确实,既能够保证编程水平不下降(或者提升),又能够保证理论上的深入性,是一件非常困难的事情啊。我的偶像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;  
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值