修改环形队列入队出队逻辑后,能否将F、R初始值设为0?
循环队列修改实现的可行性验证
在《计算机程序设计艺术》(TAOCP)第一卷2.2.2节“顺序分配”中,Donald Knuth提到循环队列的(4)(5)两种实现采用F=R=0的初始值,但使用(6a)(7a)时需设F=R=1,否则当F=0时无法检测溢出。我对(6a)(7a)进行了如下修改,请问是否仍可将F和R的初始值设为0?
func enqueue(X, Y) { if R == M { R = 0 // <- rewind to 0 } else { R = R + 1 } if R == F { return error("OVERFLOW") } X[R] = Y } func dequeue(X, &Y) { if F == R { return error("UNDERFLOW") } if F == M { F = 0 // rewind to 0 } else { F = F + 1 } Y = X[F] }
补充说明:该实现仅在调用enqueue()和dequeue()时检测队列满(OVERFLOW)或空(UNDERFLOW),无需浪费第0个元素,无需让X[1]与X[M]逻辑相邻而跳过X[0]。另外,实现循环队列有预留一个元素的技巧:容量为M的队列仅能存M-1个元素,存M个元素需分配M+1空间,优点是无需调用入队出队即可判断空满:F=R为空,F=(R+1)mod M为满。Knuth此处未用该技巧,我认为若适当修改(6a)(7a),F=R=1和F=R=0无差异,特咨询该修改方案的可行性。
结论:该修改方案完全可行,初始值设为F=R=0是合理的
核心逻辑验证
这个实现通过调整指针移动与空满判断的时机,完美区分了空队列与满队列两种指针重合的场景,具体分析如下:
- 空队列状态:初始
F=R=0时队列为空,dequeue()中先判断F==R,会直接触发UNDERFLOW,符合预期。当所有元素出队后,F最终会追上R,回到F=R的空状态。 - 入队与满队列判断:入队时先移动
R指针(包含回绕到0的处理),再检查R==F——此时若重合,说明R移动后的位置恰好是F指向的队头前一个位置,意味着所有M个存储位置已被填满,触发OVERFLOW是正确的。队列最大容量为M,没有浪费任何空间。 - 出队逻辑:出队时先判断空状态,再移动
F指针(同样包含回绕处理),然后取出对应位置的元素,确保只有队列非空时才会执行取数操作,逻辑严谨。
与Knuth原始实现的对比
你的修改本质上是将指针的起始偏移从1调整为0,循环逻辑完全自洽:
- 无需跳过
X[0]位置,充分利用了所有分配的存储空间; - 没有采用「预留一个元素」的技巧,但通过入队后判满、出队前判空的时机设计,同样准确区分了空/满状态,避免了指针重合带来的歧义。
边界场景验证
- 首次入队:
R从0变为1,R != F(0),成功存入X[1]; - 存满M个元素:当
R从M回绕到0时,此时F仍为0,触发OVERFLOW,说明已存满M个元素(X[1]到X[M]); - 全部出队:第M次出队后,
F从M回绕到0,此时F=R=0,回到空状态,触发UNDERFLOW。
内容的提问来源于stack exchange,提问作者da_miao_zi
相关产品推荐
相关产品推荐

