Single Number II

Posted codingEskimo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Single Number II相关的知识,希望对你有一定的参考价值。

The problem is similar to Single Number, two ways can solve it:

1. Traditional way: HashMap 参考之前的note

2. Bit Operation:

http://www.wengweitao.com/lintcode-single-number-i-ii-iii-luo-dan-de-shu.html

class Solution {
public:
	/**
	 * @param A : An integer array
	 * @return : An integer 
	 */
	bool isBit1(int num, int index){
        int mask = 1 << index; // left shift 1 to the bit we want to calculate eg. 0100 
        return (num & mask);  // & 0100 will set all other bit to be 0 except 1. 
                              // 1 will be same as original, so it either to be a number or 0000
    }
    
    int singleNumberII(vector<int> &A) {
        // write your code here
        int n = A.size();
        int res = 0;
        for(int i = 0; i < 32; ++i){
            int count = 0;
            for ( int j = 0; j < n; ++j){
                if(isBit1(A[j], i)){
                    count++;
                }
            }
            res |= (count % 3) << i;
        }
        return res;
    }
};

思路1:

对int的32bit的各个位中1出现的次数,对数组中所有元素的逐个进行统计,然后对每个bit对3取余,最终的结果就是只出现一次的数字。

由于x^x^x = x,无法直接利用I的方法来解。但可以应用类似的思路,即利用位运算来消除重复3次的数。以一个数组[14 14 14 9]为例,将每个数字以二进制表达:
 
1110
1110
1110
1001
_____
4331    对每一位进行求和
1001    对每一位的和做%3运算,来消去所有重复3次的数
 
public class Solution {
	/**
	 * @param A : An integer array
	 * @return : An integer 
	 */
    public int singleNumberII(int[] A) {
        // write your code here
        int n = A.length;
        int once = 0;
        int twice = 0;
        int third = 0;
        for (int i = 0; i < n; ++i){
            twice |= once & A[i]; // if it shows once, and A[i] has it again, it should set to twice;
            once ^= A[i]; //if it shows once, and A[i] has it again, it has been send to twice, once has to be set back to zero;
            third = once & twice; // if it shows once, and it shows twice, it shows three times;
            once &= ~third; //once it set to three times, set back once/twice to be zero;
            twice &= ~third;
        }
        return once;
    }
}

思路2: 利用掩码的思想。

  • 变量once作为一个掩码,若once的二进制表示中的一个位置为1,表示目前使该位为1的数只出现一次
  • 变量twice作为一个掩码,若twice的二进制表示中的一个位置为1,表示目前使该位置为1的数出现了2次
  • 变量third作为一个掩码,若third的二进制表示中的一个位置为1,表示目前使该位置为1的数出现了3次
  • 若之前出现一次(表示为once中对应的位为1),A[i]中又出现(表示为A[i]中对应的位置为1),那么twice中对应的位就为1;
  • 若之前的once中出现,而A[i]中又出现,那么就不是出现一次,将对应的位取0
  • once中出现一次,twice出现两次,也就是出现了3次
  • 需要将once和twice中出现3次的位都置为0

以上的代码涉及到比较多的位运算,可能不大好理解。举个具体的例子:

once: 100101 使第1、3、6个bit为1的数出现3次

twice: 010101 使第1、3、5个bit为1的数出现了2次

third: 000101 根据once和twice可以得到third,第1和3个bit为1的数出现了3次。

以上算法的时间复杂度为O(n),空间复杂度为O(1)。

 

 

扩展一:

给定一个包含n个整数的数组,除了一个数出现二次外所有的整数均出现三次,找出这个只出现二次的整数。ones记录1出现一次的数,twos记录1出现2次的数,容易知道twos记录的即是最终结果。

扩展二:

给定一个包含n个整数的数组,有一个整数x出现b次,一个整数y出现c次,其他所有的数均出现a次,其中b和c均不是a的倍数,找出x和y。使用二进制模拟a进制,累计二进制位1出现的次数,当次数达到a时,对其清零,这样可以得到b mod a次x,c mod a次y的累加。遍历剩余结果(用ones、twos、fours...变量表示)中每一位二进制位1出现的次数,如果次数为b mod a 或者 c mod a,可以说明x和y的当前二进制位不同(一个为0,另一个为1),据此二进制位将原数组分成两组,一组该二进制位为1,另一组该二进制位为0。这样问题变成“除了一个整数出现a1次(a1 = b 或 a1 = c)外所有的整数均出现a次”,使用和上面相同的方式计算就可以得到最终结果,假设模拟a进制计算过程中使用的变量为ones、twos、fours...那么最终结果可以用ones | twos | fours ...表示。

 

以上是关于Single Number II的主要内容,如果未能解决你的问题,请参考以下文章

Single Number II

LeetCode136 Single Number, LeetCode137 Single Number II, LeetCode260 Single Number III

137. Single Number II

137. Single Number II

Single Number II

LeetCode-Single Number II