原题如下:
"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?"
Best answer 如下:
int singleNumber(int A[], int n) {
int ones = 0, twos = 0, threes = 0;
for (int i = 0; i < n; i++) {
twos |= ones & A[i];//L1, 来0不变(安全)
ones ^= A[i];//L2, 来1取反,来0不变(安全)
threes = ones & twos;//L3, 后两位全1时为1,否则为0,。 确定threes位
ones &= ~threes;//L4, threes是1时归零,否则不变。 确定ones位
twos &= ~threes;//L5, threes是1时归零,否则不变。 确定twos位
}
return ones;
}
我们用L1~L5来标记关键代码以便叙述。
分析如下:
三个变量的意义摘录原文:
ones
as a bitmask to represent the ith bit had appeared once.twos
as a bitmask to represent the ith bit had appeared twice.threes
as a bitmask to represent the ith bit had appeared three times.
每当一个0到来时,该位不变,暂且称之为安全性。L1,L2行体现了两种安全操作,即“|= & ”和异或。
现在在安全性基础上分析当1到来时的变化规律,观察三个位变化规律,如下图:
我们取一个周期,有a,b, c,d四行,d回到了a;threes, twos, ones三列。表面是简单的循环移位,其实不简单。
对于上述提到的安全操作异或,来1时置反。通过仔细观察,可以给ones用,得到ones位的规律是"0 1 0 1变化,当threes为1时归零。"分别由L2,L4实现。
对于另一个安全操作“|= &”,自然想到给twos用。观察L1得twos位的规律是“来0时不变(安全性);来1时,ones位为0则不变,为1则置1”。由L1实现。通过求索,可确定L1,L2的先后规律。。当然,和ones一样,还要补上L5的“当threes为1时归零”。
threes的规律比较简单,当后两位在执行完L1,L2后全1时置1,否则为0,如L3所示。
L4,L5只在three位为1时起作用,如图中d行红框所示。b,c行无中间过渡值。
总结
学到了两个“安全操作”:异或 和 "|= & "。
one, two ,three的变化规律相互依赖,其中one和异或是突破口。
本文旨在对这段代码的理解分析,至于大神是怎么想出这个算法的,只能自己体会了。。