关于leetcode的单号II的问题是:
给定一个整数数组,除了一个元素外,每个元素都会出现三次。找一个单一的。 注意: 您的算法应具有线性运行时复杂性。你可以在不使用额外内存的情况下实现吗?
实际上,我已经从网站上找到了解决方案,解决方法是:
public int singleNumber(int[] A) {
int one = 0, two = 0;
for (int i = 0; i < A.length; i++) {
int one_ = (one ^ A[i]) & ~two;
int two_ = A[i] & one | ~A[i] & two;
one = one_;
two = two_;
}
return one;
}
但是,我不知道为什么这段代码可以工作,实际上我不知道第一次看到这个问题时想到这个问题的方法呢?任何帮助。 THX!
答案 0 :(得分:6)
这个想法是将数字重新解释为GF(3)上的向量。原始数字的每个位都成为向量的一个组成部分。重要的部分是对于GF(3)向量空间中的每个向量v,求和v + v + v得到0.因此,所有向量的和将保留唯一向量并取消所有其他向量。然后,结果再次被解释为一个数字,它是所需的单个数字。
GF(3)向量的每个分量可以具有值0,1,2,其中加法执行mod 3.“1”捕获低位,“2”捕获结果的高位。因此,尽管该算法看起来很复杂,但它所做的只是“无需进位的数字加法模3”。
答案 1 :(得分:5)
因此,我遇到了一些编码问题,并在相当长的一段时间内坚持了下来,在Google上进行了大量研究之后,通过不同的帖子和门户网站,我已经理解了这个问题。我会尽力解释它。
问题有3个解决方案:
public class SingleNumberII {
/*
* Because the max integer value will go upto 32 bits
* */
private static final int INT_SIZE = 32;
public int singleNumber(final int[] A) {
int result = 0;
for(int bitIterator = 0; bitIterator < INT_SIZE; bitIterator++) {
int sum = 0, mask = (1 << bitIterator);
/*
* Mask:
* 1 << any number means -> it will add that number of 0 in right of 1
* 1 << 2 -> 100
* Why we need mask? So when we do addition we will only count 1's,
* this mask will help us do that
* */
for(int arrIterator = 0; arrIterator < A.length; arrIterator++) {
/*
* The next line is to do the sum.
* So 1 & 1 -> 0
* 1 & 0 -> 0
* The if statement will add only when there is 1 present at the position
* */
if((A[arrIterator] & mask) != 0) {
sum++;
}
}
/*
* So if there were 3 1's and 1 0's
* the result will become 0
* */
if(sum % 3 == 1) {
result |= mask;
}
}
/*So if we dry run our code with the above example
* bitIterator = 0; result = 0; mask = 1;
* after for loop the sum at position 0 will became 3. The if
* condition will be true for 5 as - (101 & 001 -> 001) and false for 6
* (110 & 001 -> 000)
* result -> 0 | 1 -> 0
*
* bitIterator = 1; result = 0; mask = 10;
* after for loop the sum at position 1 will became 1. The if
* condition will be true for 6 as - (110 & 010 -> 010) and false for 5
* (101 & 010 -> 000)
* result -> 00 | 10 -> 10
*
* bitIterator = 2; result = 10; mask = 100;
* after for loop the sum at position 2 will became 4. The if
* condition will be true for 6 as - (110 & 100 -> 100) and true for 5
* (101 & 100 -> 100)
* result -> 10 | 100 -> 110 (answer)
* */
return result;
}
}
正如我们看到的那样,这不是最佳解决方案,因为我们不需要对其进行32次以上的迭代,而且也没有那么概括。这就是访问我们的下一个方法的原因。
public int singleNumberOptimized(int[] A) {
int one = 0, two = 0;
/*
* Two sets to maintain the count the number has appeared
* one -> 1 time
* two -> 2 time
* three -> not in any set
* */
for(int arrIterator = 0; arrIterator < A.length; arrIterator++){
/*
* IF one has a number already remove it, and it does not have that number
* appeared previously and it is not there in 2 then add it in one.
* */
one = (one ^ A[arrIterator]) & ~two;
/*
* IF two has a number already remove it, and it does not have that number
* appeared previously and it is not there in 1 then add it in two.
* */
two = (two ^ A[arrIterator]) & ~one;
}
/*
* Dry run
* First Appearance : one will have two will not
* Second Appearance : one will remove and two will add
* Third Appearance: one will not able to add as it is there in two
* and two will remove because it was there.
*
* So one will have only which has occurred once and two will not have anything
* */
return one;
}
答案 2 :(得分:4)
乍一看,代码似乎很棘手,很难理解。 但是,如果以布尔代数形式考虑问题,一切都会变得很清楚。
我们需要做的是存储每一位的1's
数。由于32位中的每一个都遵循相同的规则,因此我们只需要考虑1位。我们知道一个数字最多出现3次,因此我们需要 2位进行存储。现在我们有4个状态,00、01、10和11,但是我们只需要3个状态。
在您的解决方案中,选择01(代表1)和10(代表2)。令one
代表第一位,two
代表第二位。然后,我们需要为one_
和two_
设置规则,以便它们按我们希望的那样工作。完整的循环是00-> 10-> 01-> 00(0-> 1-> 2-> 3/0)。
最好制作 Karnaugh地图,也就是 K地图。对于卡诺地图Ref.
在
之后的A[i]
,two
,one
和two_
,one_
的值
0 0 0 | 0 0
0 0 1 | 0 1
0 1 0 | 1 0
0 1 1 | X X
1 0 0 | 0 1
1 0 1 | 1 0
1 1 0 | 0 0
1 1 1 | X X
在这里 X 表示我们不在乎这种情况,或者只关心最终值2和1,无论它们的输出是1还是什么也可以考虑,结果将是相同的,并且第四和第八种情况不存在,因为两种情况不能同时为一种。
如果您正在考虑我如何提出上表。我将解释其中之一。考虑第七种情况,当A [i]为1时,两个为1,即已经存在重复两次的A [i]。最后有3个A [i]。由于其中有3个,因此two_
和one_
都应重置为0。
考虑使用one_
对于两种情况(即第二种情况和第五种情况),其值为 1 。采取 1 与考虑在K-map中的最小项相同。
one_ = (~A[i] & ~two & one) | (A[i] & ~two & ~one)
如果~two
很普遍,那么
(~A[i] & one) | (A[i] & ~one) will be same as (A[i]^one)
然后
one_ = (one ^ A[i]) & ~two
考虑使用two_
对于两种情况(即第三种情况和第六种情况),其值为 1 。采取 1 与考虑在K-map中的最小项相同。
two_ = (~A[i] & two & ~one) | (A[i] & ~two & one)
对计算出的 two _ 进行位操作将解决该问题。但是,正如您提到的
two_ = (A[i] & one) | (~A[i] & two)
考虑到无关即 X ,对于上述所有情况,考虑到 X < / strong>不会影响我们的解决方案。
考虑使用K-map
并考虑使用X
对于两种情况(即第三和第六种情况),其值为 1 ;对于两种情况(即第四和第八种情况),其值为 X 。现在,考虑最小期限。
two_
现在,以上面的表达式中的公共(A&one)和(〜A&Two),您将剩下(1 |〜2)和(1 |〜one)1。因此,我们'将被留下
two_ = (~A[i] & two & ~one) | (A[i] & ~two & one) | (~A[i] & two & one) | (A[i] & two & one)
答案 3 :(得分:3)
有三种状态:0,1,2
所以不能使用单个位,必须使用高/低位来表示它们:00,01,10
这是逻辑:
高/低00 01 10
x = 0 00 01 10
x = 1 01 10 00
高是高和低的函数。
如果低== 1则高= x,否则高=高&amp; 〜X
我们有
高=低&amp; x |高&amp; 〜X
这等于你:“int two_ = A [i]&amp; one | ~A [i]&amp; two;”
同样,我们作为高和低的函数都很低:
如果高== 1则低= ~x,否则低=低XOR x
答案 4 :(得分:0)
我有一个更直接的解决方案:
int singleNumber(int A[], int n) {
int one = 0, two = 0, three = ~0;
for(int i = 0; i < n; ++i) {
int cur = A[i];
int one_next = (one & (~cur)) | (cur & three);
int two_next = (two & (~cur)) | (cur & one);
int three_next = (three & (~cur)) | (cur & two);
one = one_next;
two = two_next;
three = three_next;
}
return one;
}
答案 5 :(得分:0)
首先是我的头脑,它更大但更容易理解。只需实现3的附加模式。
*
class Solution {
public:
int sum3[34], bit[33];
int singleNumber(int A[], int n) {
int ans(0);
for(int i=0;i<33;i++){
bit[i + 1] = 1<<i;
}
int aj;
for(int i=0;i<n;i++){
for(int j=1;j<33;j++){
aj = abs(A[i]);
if(bit[j] & aj) sum3[j]++;
}
}
for(int i=0;i<33;i++){
sum3[i] %= 3;
if(sum3[i] == 1) ans += bit[i];
}
int positve(0);
for(int i=0;i<n;i++){
if(A[i] == ans){
positve++;
}
}
if(positve%3 == 1)
return ans;
else return -ans;
}
};
*
答案 6 :(得分:0)
这是另一种解决方案。
public class Solution {
public int singleNumber(int[] nums) {
int p = 0;
int q = 0;
for(int i = 0; i<nums.length; i++){
p = q & (p ^ nums[i]);
q = p | (q ^ nums[i]);
}
return q;
}
}
来自this blog post的分析。