这是一个简单的 C 解决方案,应该说明该技术。为了抽象出任何繁琐的文件 I/O 细节,我假设存在以下三个函数:
unsigned long next_number (void)
从文件中读取一个数字并返回它。再次调用时,将返回文件中的下一个数字,依此类推。遇到文件结尾时的行为未定义。
int numbers_left (void)
如果有更多数字可供使用 读取next_number()
,则返回 true 值,如果已到达文件末尾,则返回 false。
void return_to_start (void)
将读取位置倒回到文件的开头,以便下一次调用next_number()
返回文件中的第一个数字。
我还假设它unsigned long
至少是 32 位宽,这是符合 ANSI C 实现的要求;现代 C 程序员可能更喜欢使用uint32_t
from stdint.h
。
鉴于这些假设,以下是解决方案:
unsigned long count_numbers_in_range (unsigned long min, unsigned long max) {
unsigned long count = 0;
return_to_start();
while ( numbers_left() ) {
unsigned long num = next_number();
if ( num >= min && num <= max ) {
count++;
}
}
return count;
}
unsigned long find_missing_number (void) {
unsigned long min = 0, max = 0xFFFFFFFF;
while ( min < max ) {
unsigned long midpoint = min + (max - min) / 2;
unsigned long count = count_numbers_in_range( min, midpoint );
if ( count < midpoint - min + 1 ) {
max = midpoint; // at least one missing number below midpoint
} else {
min = midpoint; // no missing numbers below midpoint, must be above
}
}
return min;
}
需要注意的一个细节是,这是计算和min + (max - min) / 2
平均值的安全方法;由于像看似简单的可能那样溢出中间值,它不会产生虚假结果。min
max
(min + max) / 2
此外,即使使用递归来解决这个问题很诱人,我还是选择了迭代解决方案,原因有两个:首先,因为它(可以说)更清楚地显示了实际正在做什么,其次,因为任务是最小化内存使用,大概也包括堆栈。
最后,很容易优化此代码,例如,一旦count
等于 0 就返回,通过一次计算范围的两半中的数字并选择具有更多缺失数字的数字,或者甚至将二分搜索扩展到n元搜索一些n > 2 以减少通过次数。但是,为了使示例代码尽可能简单,我没有进行此类优化。如果您愿意,您可能想尝试修改代码,使其最多需要 8 次遍历文件,而不是当前的 32 次。(提示:使用 16 元素数组。)