137. Single Number II

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去解,每出现一次,key的value加1,最后遍历hashmap,找到value等于一的key。代码如下:

public class Solution {
    public int singleNumber(int[] nums) {
        HashMap<Integer, Integer> hm = new HashMap <Integer, Integer>();
        int result = 0;
        for (int i = 0; i < nums.length; i++) {
            if (hm.containsKey(nums[i])) {
                hm.put(nums[i],hm.get(nums[i]) + 1);
            } else {
                hm.put(nums[i], 1);
            }
        }
        Iterator iter = hm.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Object key = entry.getKey();
            Object val = entry.getValue();
            if ((Integer)val == 1) {
                result = (Integer)key;
            }
        }
        return result;
    }
}
这道题提倡的做法是位操作,int是32位,每一位进行一次连加,然后对3取余,结果要么是0,要么是1。最后把每位的结果shift到相应位上。

The usual bit manipulation code is bit hard to get and replicate. I like to think about the number in 32 bits and just count how many 1s are there in each bit, and sum %= 3 will clear it once it reaches 3. After running for all the numbers for each bit, if we have a 1, then that 1 belongs to the single number, we can simply move it back to its spot by doing ans |= sum << i;

This has complexity of O(32n), which is essentially O(n) and very easy to think and implement. Plus, you get a general solution for any times of occurrence. Say all the numbers have 5 times, just do sum %= 5.

代码如下:

public int singleNumber(int[] nums) {
    int ans = 0;
    for(int i = 0; i < 32; i++) {
        int sum = 0;
        for(int j = 0; j < nums.length; j++) {
            if(((nums[j] >> i) & 1) == 1) {
                sum++;
                sum %= 3;
            }
        }
        if(sum != 0) {
            ans |= sum << i;
        }
    }
    return ans;
}

还有一种解法,类似于状态机:

The code seems tricky and hard to understand at first glance.
However, if you consider the problem in Boolean algebra form, everything becomes clear.

What we need to do is to store the number of '1's of every bit. Since each of the 32 bits follow the same rules, we just need to consider 1 bit. We know a number appears 3 times at most, so we need 2 bits to store that. Now we have 4 state, 00, 01, 10 and 11, but we only need 3 of them.

In this solution, 00, 01 and 10 are chosen. Let 'ones' represents the first bit, 'twos' represents the second bit. Then we need to set rules for 'ones' and 'twos' so that they act as we hopes. The complete loop is 00->10->01->00(0->1->2->3/0).

  • For 'ones', we can get 'ones = ones ^ A[i]; if (twos == 1) then ones = 0', that can be tansformed to 'ones = (ones ^ A[i]) & ~twos'.

  • Similarly, for 'twos', we can get 'twos = twos ^ A[i]; if (ones* == 1) then twos = 0' and 'twos = (twos ^ A[i]) & ~ones'. Notice that 'ones*' is the value of 'ones' after calculation, that is why twos is
    calculated later.


Here is another example. If a number appears 5 times at most, we can write a program using the same method. Now we need 3 bits and the loop is 000->100->010->110->001. The code looks like this:

int singleNumber(int A[], int n) {
	int na = 0, nb = 0, nc = 0;
	for(int i = 0; i < n; i++){
		nb = nb ^ (A[i] & na);
		na = (na ^ A[i]) & ~nc;
		nc = nc ^ (A[i] & ~na & ~nb);
	}
	return na & ~nb & ~nc;
}

Or even like this:

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

I hope all these above can help you have a better understand of this problem.

如果看不懂上面的解法,看下面的会更容易理解,利用真值表求解,厉害,数电没白学。

I think many coder confused how to generate the code.

This is my analysis, use Truth table to generate code:

Consider every bit in a number, if this bit appear 3 times then set it to zero.

so here is 3 state of one bit, we can use two bit to indicate it.

we use 00 -> 01 -> 10 -> 00...

00: bit 1 appear 0 time.

01: bit 1 appear 1 time.

10: bit 1 appear 2 times.

so every bit 1 appear 3 times, the state will go to 00

but for the single number, every bit 1 in it, its status is 01

we can use two integer to represent the status, named first, second

Now problem is how to generate the code to change 'first' and 'second'

I use truth table to generate the code, like this:

count every '1' bit, 3 times to zero

so we need 3 status, 2 bit is enough

state: 00 -> 01 -> 10 -> 00....

so we can draw a truth table of first bit

A: first bit

B: second bit

C: input bit

A B C NA NB

0 0 0 0 0

0 0 1 0 1

0 1 0 0 1

0 1 1 1 0

1 0 0 1 0

1 0 1 0 0

1 1 0 not define

1 1 1 not define

so

NA = ~ABC + A~B~C

NB = ~A~BC + ~AB~C = ~A(B^C)

NB is first, NA is second, so we should use last first when we calc next second

code is :

int first = 0, second = 0;

for (int i = 0; i < n; ++i)

{

int lastFirst = first;

first = ~second & (first ^ A[i]);

second = (~second&lastFirst&A[i])|(second&~lastFirst&~A[i]);

}

return first;



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值