这是一些产生与您的示例类似的输出的 Python 代码:
def f(low, high):
ranges = collections.deque([(low, high)])
while ranges:
low, high = ranges.popleft()
mid = (low + high) // 2
yield mid
if low < mid:
ranges.append((low, mid))
if mid + 1 < high:
ranges.append((mid + 1, high))
例子:
>>> list(f(0, 20))
[10, 5, 15, 2, 8, 13, 18, 1, 4, 7, 9, 12, 14, 17, 19, 0, 3, 6, 11, 16]
该low, high
范围不包括端点,这是 Python 中的约定,因此结果包含从 0 到 19 的数字。
该代码使用 FIFO 来存储仍需要处理的子范围。FIFO 以全范围初始化。在每次迭代中,弹出下一个范围并产生中点。然后,如果当前范围的下子范围和上子范围非空,则将它们附加到 FIFO。
编辑:这是 C99 中完全不同的实现:
#include <stdio.h>
int main()
{
const unsigned n = 20;
for (unsigned i = 1; n >> (i - 1); ++i) {
unsigned last = n; // guaranteed to be different from all x values
unsigned count = 1;
for (unsigned j = 1; j < (1 << i); ++j) {
const unsigned x = (n * j) >> i;
if (last == x) {
++count;
} else {
if (count == 1 && !(j & 1)) {
printf("%u\n", last);
}
count = 1;
last = x;
}
}
if (count == 1)
printf("%u\n", last);
}
return 0;
}
这通过使用一些技巧来确定一个整数是否已经在较早的迭代中出现,从而避免了 FIFO 的必要性。
您也可以轻松地在 C 中实现原始解决方案。由于您知道 FIFO 的最大大小(我猜它类似于 (n+1)/2,但您需要仔细检查),您可以使用环形缓冲区保存排队的范围。
编辑 2:这是 C99 中的另一个解决方案。它被优化为只进行一半的循环迭代,并且只使用位运算和加法,没有乘法或除法。它也更简洁,并且它不包含0
在结果中,因此您可以按照最初的意图在开始时进行此操作。
for (int i = 1; n >> (i - 1); ++i) {
const int m = 1 << i;
for (int x = n; x < (n << i); x += n << 1) {
const int k = x & (m - 1);
if (m - n <= k && k < n)
printf("%u\n", x >> i);
}
}
(这是我从一开始就打算编写的代码,但我花了一些时间来理解它。)