Single Number题解整理

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

 

如果是其余元素均出现两次,这个题目很容易解决,我们直接可以用xor,把所有的数异或之后的结果,便是出现一次的数字。但是这个题目其余元素出现的次数是三次,如果数组中的元素都是三个三个出现的,那么从二进制表示的角度,每个位上的1加起来,应该可以整除3。

基本方法:

如果有一个数x只出现一次,会是什么情况呢?

    如果某个特定位上的1加起来,可以被3整除,说明对应x的那位是0,因为如果是1,不可能被3整除

    如果某个特定位上的1加起来,不可以被3整除,说明对应x的那位是1

根据上面的描述,我们可以开辟一个大小为32的数组,第0个元素表示,A中所有元素的二进制表示的最低位的和,依次类推。

int get_high_bit_one(int num){
	for (int i = 31; i >=0; --i){
		if (!(num & (1<<i)))
			continue;
		return i;
	}
}

int find_once_number(int arr[], int len){
	int* bit_arr = new int[32];
	int num = 0;

	if (bit_arr != NULL){
		memset(bit_arr, 0, 32*sizeof(int));

		for (int index = 0; index < len; ++index){
			int bit_nums = get_high_bit_one(arr[index]);
			for (int bit_idx = 0; bit_idx <= bit_nums; ++bit_idx){
				bit_arr[bit_idx] +=  (arr[index] & (1<<bit_idx))>>bit_idx;
			}
		}

		for (int i = 0; i < 32; ++i){
			if (bit_arr[i]%3)
				bit_arr[i] = 1;
			else
				bit_arr[i] = 0;
		}

		for (int i = 0; i < 32; ++i){
			num += bit_arr[i]*(1<<i);
		}

		delete[] bit_arr;
	}
	return num;
}


位操作is coming…

有没有更加简洁的方法呢?我们可以从位操作上入手这个问题。用二进制模拟三级制的运算

B1

B0

Input

B1`

B0`

0

0

1

0

1

0

1

1

1

0

1

0

1

0

0

0

0

0

0

0

0

1

0

0

1

1

0

0

1

0

通过位之间的转换,我们可以得到如下代码.

    int singleNumber(int A[], int n) {
        int one = 0, two = 0;
        for (int i = 0; i < n; i++) {
            int one_ = (one ^ A[i]) & ~two;
            int two_ = A[i] & one | ~A[i] & two;
            one = one_;
            two = two_;
        }
        return one;
    }


另外的方法:

用ones记录到当前计算的变量为止,二进制1出现“1次”(mod 3 之后的 1)的数位。用twos记录到当前计算的变量为止,二进制1出现“2次”(mod 3 之后的 2)的数位。当ones和twos中的某一位同时为1时表示二进制1出现3次,此时需要清零。即用二进制模拟三进制计算。最终ones记录的是最终结果。

int singleNumber(int A[], int n) {
    int ones = 0, twos = 0, xthrees = 0;
    for(int i = 0; i < n; ++i) {
        twos |= (ones & A[i]);
        ones ^= A[i];
        xthrees = ~(ones & twos);
        ones &= xthrees;
        twos &= xthrees;
    }
    return ones;
}


更好的方法,而且可以扩展到多位计算的方法。

int getSingleNumber_v2(int a[], int len){
	if (a == NULL)
		return 0;
	int x0 = ~0, x1 = 0, x2 = 0, tmp;
	for (int i = 0; i < len; ++i){
		tmp = x2;
		x2 = (x1 & a[i]) | (x2 & ~a[i]);
        x1 = (x0 & a[i]) | (x1 & ~a[i]);
        x0 = (tmp & a[i]) | (x0 & ~a[i]);
	}
	return x1;
}

利用上述方法可以解决这个扩展问题:

Q:Given an array ofintegers, every element appears k times except for one. Find that single one who appears l times.

int getSingleNumber_v3(int A[], int len, int k, int l){
    if (A == NULL) return 0;
    int t;
    int* x = new int[k];
    memset(x, 0, k*sizeof(int));
    x[0] = ~0;

    for (int i = 0; i < len; i++) {
        t = x[k-1];
        for (int j = k-1; j > 0; j--) {
            x[j] = (x[j-1] & A[i]) | (x[j] & ~A[i]);
        }
        x[0] = (t & A[i]) | (x[0] & ~A[i]);
    }
    return x[l];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值