问题描述
最近一位比我年长的开发人员给了我一个看似很小的 JavaScript 编程挑战。
它是这样的:
编写代码以在新数字插入跟踪器类时持续跟踪最高、最低和平均温度。
优化空间和时间。
以下是我的解决方案(您可以找到完整的源代码,包括测试 here)。
class TempTracker {
constructor() {
this.temperatures = [];
}
all() {
return [...this.temperatures];
}
clear() {
this.temperatures.length = 0;
}
count() {
return this.temperatures.length;
}
insert(temperature) {
if (typeof temperature !== 'number' || Number.isNaN(temperature)) {
throw new TypeError('must be a number');
}
this.temperatures.push(temperature);
}
add(temperature) {
this.insert(temperature);
}
get length() {
return this.count();
}
get lowest() {
return this.check((temperatures) => Math.min(...temperatures));
}
get highest() {
return this.check((temperatures) => Math.max(...temperatures));
}
get average() {
return this.check((temperatures,length) => {
const totalTemperature = temperatures.reduce(
(acc,temperature) => acc + temperature,// eslint-disable-next-line
0
);
return Number((totalTemperature / length).toFixed(2));
});
}
// should be private method
check(callback) {
if (typeof callback !== 'function') {
throw new TypeError('callback must be a function');
}
if (this.length === 0) {
throw new Error('add at least one temperature value');
}
return callback(this.temperatures,this.length);
}
}
虽然代码确实按预期工作,但我被告知我的代码没有针对空间和时间进行优化,因为随着输入增长到数百万或数十亿的温度,用于处理的内存将耗尽,并且还提到了其他一些内容的行,需要线性时间 O(?).
我对数据结构和算法的了解充其量是非常模糊的,我真的在努力改进这方面。
当我问我可以做些什么来改进代码时,我被告知可以使用数组以外的其他东西来存储温度列表,然后让我思考哪个对这个用例最有意义。
在 JavaScript 中可用的数据结构(至少我知道)中,我唯一能想到的是 Sets 和 Float32Arrays。
我能否获得关于哪个更好以及为什么更好的指导,以及如何改进我的解决方案以“优化空间和时间”?
解决方法
此任务不需要任何复杂的数据结构,您只需要跟踪:
- 迄今为止所见的最高温度
- 迄今为止所见的最低温度
- 目前看到的温度总和
- 到目前为止看到的温度数
这需要 O(1) 空间,并且每个所述操作需要 O(1) 时间(要获得平均值,将温度总和除以温度数量)。
,对于给定的要求,您实际上并不需要存储所有单独的温度……不需要使用 all
方法。
你实际上只需要维护最小值、最大值、总和和计数。有了这 4 个数字,您就可以完成所需的一切:
class TempTracker {
constructor() {
this._low = Infinity;
this._high = -Infinity;
this._count = 0;
this._sum = 0;
}
insert(temperature) {
if (typeof temperature !== 'number' || Number.isNaN(temperature)) {
throw new TypeError('must be a number');
}
this._low = Math.min(temperature,this._low);
this._high = Math.max(temperature,this._high);
this._count++;
this._sum += temperature;
}
get lowest() {
if (!this._count) throw new Error("no data");
return this._low;
}
get highest() {
if (!this._count) throw new Error("no data");
return this._high;
}
get average() {
if (!this._count) throw new Error("no data");
return Math.round(this._sum * 100 / this._count) / 100;
}
}