采取编码采访:
如何在O(1)时间复杂度中实现具有increment
的无限二进制计数器?
我想要计算最右边0
的第一和第二位置,但我不确定如何实现它。
“无限计数器”意味着您可以无限次地增加(大于MAX_INT)。
答案 0 :(得分:7)
对于二元计数器......
如果你想让计数器处于“正常”位模式,你基本上不能 - 至少不能总是 O(1)而不是摊销 O(1)。
如果它是一个无限计数器,它可以有任意多位。这意味着你可以有多个N位,所有这些都是1.增加该计数器意味着将所有这些位设置为0,这可以合理地假设为O(N)操作。
我们可以在“正常”计算中考虑增量为O(1)的唯一原因是通常处理固定大小的类型,我们可以说(例如)“最多32位需要更改 - 这是一个常数,所以它可以说是一个O(1)操作。“
只是一个反击......
另一方面,如果只是希望能够在O(1)时间内增加,那么你拥有无限的内存,并且你不关心恢复该值需要多长时间,可以做,只需有效地使用长度为计数器大小的链表。
例如,在C#中:
public DodgySolution
{
public static DodgySolution Zero = new DodgySolution(null);
private DodgySolution tail;
private DodgySolution(DodgySolution tail)
{
this.tail = tail;
}
// This bit is O(1)
public DodgySolution Increment()
{
return new DodgySolution(this);
}
// This bit isn't...
public BigInteger ToBigInteger()
{
return tail == null ? BigInteger.Zero
: BigInteger.One + tail.ToBigInteger();
}
}
即使这假设引用赋值是O(1) - 但是对于无限数量的对象可能会变得棘手......
答案 1 :(得分:3)
因此,直接实施已经摊销了O(1)表现。唯一的问题是你需要可变存储。
当查看数字n
的单个增量运算时,平均时间为O(1),但最差情况为O(log(n))。内存使用率为O(log(n))。
var counter=new List<bool>{false};
void Inc()
{
while(counter[i])
{
counter[i]=false;
i++;
}
if(i==counter.Length)
counter.Add(true);
else
counter[i]=true;
}
答案 2 :(得分:2)
如果问题仅要求增加O(1)计数器而没有任何其他限制,您的计数器可以实现为数字的链接列表,项目总和是您的计数器的值。
如果前面的值大于(Max-1),则递增相当于向最后一项添加1或添加新项= 1。
由于您总是最多检查列表中的2个项目,因此递增将为O(1)
请不要尝试用闪亮的新柜台做其他算术:D
答案 3 :(得分:-1)
我的尝试:
我们会在连续的1
或0
上保留一个汇总。
意味着111000111是&lt; 1,0&gt; &LT; 3,1&GT; &LT; 3,0&GT; &LT; 3,1&GT;
我可以使用以下DS表示:
节点列表{digit:bool,counter:long}
1)如果第一个批量为1。它变为0的大部分并将下一个0转为1。
我们现在检查是否可以聚合1的批量。
2)如果第一个批量为0,我们将第一个数字设为1.并查看我们是否可以汇总1个。
示例A:
意味着111000111是&lt; 1,0&gt; &LT; 3,1&GT; &LT; 3,0&GT; &LT; 3,1&GT;
阅读:三个1
位数,三个0
位数,三个1
位数,一个0
位数
增量()
&LT; 1,0&GT; &LT; 3,1&GT; &LT; 2,0&GT; &LT; 1,1 GT; &LT; 3,0&GT;
例B:
&LT; 1,0&GT; &LT; 3,1&GT; &LT; 1,0&GT; &LT; 3,1&GT;
增量()
&LT; 1,0&GT; &LT; 3,1&GT; &LT; 1,1 GT; &LT; 3,0&GT;
聚合:
&LT; 1,0&GT; &LT; 4,1&GT; &LT; 3,0&GT;
始终存在常数变化(最多0位数)
并转换1
s的大部分内容只是为了摆弄布尔成员。这是常数