【发布时间】:2020-02-06 13:52:31
【问题描述】:
我正在尝试编写一个足够快的算法来将区间与值合并。
例如有3个区间(包括from和to):
{from:1, to: 3, amount: 1}{from:5, to: 7, amount: 1}{from:2, to: 6, amount: 1}
结果:
{from:1, to: 1, amount: 1}{from:2, to: 3, amount: 2}{from:4, to: 4, amount: 1}{from:5, to: 6, amount: 2}{from:7, to: 7,amount: 1}
当间隔数达到 5000 时,我的算法中的计算可能需要大约一分钟。 (在合并的每个新间隔与大量其他间隔重叠的情况下)。 也许有人可以建议一些适合此任务的已知算法,或者他已经遇到过类似的问题并会分享他的解决方案?
function mergeingIntervals(intervals: IInterval[]): IInterval[] {
//sort by length
intervals.sort((a, b) => b.to - b.from - a.to + a.from);
const result = [intervals[0]];
intervals.forEach(interval => addInterval(result, interval));
result.sort((a, b) => a.from - b.from);
return result;
}
function addInterval(result: IInterval[], interval: IInterval): void {
const firstIntersectionIndex = result.findIndex(row =>
row.from >= interval.from && row.to <= interval.to ||
row.from >= interval.from && row.from <= interval.to ||
row.from <= interval.from && row.to >= interval.to ||
row.to >= interval.from && row.to <= interval.to,
);
if (firstIntersectionIndex === -1) {
const indexToInsertInto = result.findIndex(row => row.from > interval.to);
if (indexToInsertInto !== -1) {
result.splice(indexToInsertInto, 0, interval);
} else {
result.unshift(interval);
}
} else {
const valueToMergeInto = result[firstIntersectionIndex];
if (valueToMergeInto.from <= interval.from && valueToMergeInto.to >= interval.to) {
const newIntervals: IInterval[] = [];
if (valueToMergeInto.from !== interval.from) {
newIntervals.push({
from: valueToMergeInto.from,
to: subtractHour(interval.from),
value: valueToMergeInto.value,
});
}
newIntervals.push({
from: interval.from,
to: interval.to,
value: interval.value + valueToMergeInto.value,
});
if (valueToMergeInto.to !== interval.to) {
newIntervals.push({
from: addHour(interval.to),
to: valueToMergeInto.to,
value: valueToMergeInto.value,
});
}
result.splice(firstIntersectionIndex, 1, ...newIntervals);
} else {
let count = 1;
result.slice(firstIntersectionIndex).forEach(sumValue => {
if (sumValue.to <= interval.to) {
count++;
return false;
} else {
return true;
}
});
if (count < 5) {
result.splice(firstIntersectionIndex, 1);
const newIntervals: IInterval[] = [];
if (interval.from < valueToMergeInto.from) {
newIntervals.push({
from: interval.from,
to: subtractHour(valueToMergeInto.from),
value: interval.value,
}, {
from: valueToMergeInto.from,
to: Math.min(valueToMergeInto.to, interval.to),
value: valueToMergeInto.value + interval.value,
});
if (interval.to !== valueToMergeInto.to) {
newIntervals.push({
from: addHour(Math.min(valueToMergeInto.to, interval.to)),
to: Math.max(valueToMergeInto.to, interval.to),
value: valueToMergeInto.to < interval.to ? interval.value : valueToMergeInto.value,
});
}
} else {
if (interval.from !== valueToMergeInto.from) {
newIntervals.push({
from: valueToMergeInto.from,
to: subtractHour(interval.from),
value: valueToMergeInto.value,
});
}
newIntervals.push({
from: interval.from,
to: Math.min(interval.to, valueToMergeInto.to),
value: interval.value + valueToMergeInto.value,
});
if (interval.to !== valueToMergeInto.to) {
newIntervals.push({
from: addHour(Math.min(valueToMergeInto.to, interval.to)),
to: Math.max(valueToMergeInto.to, interval.to),
value: valueToMergeInto.to < interval.to ? interval.value : valueToMergeInto.value,
});
}
}
newIntervals.forEach(period => addInterval(result, period));
} else {
const newIntervals: IInterval[] = [];
if (interval.from < valueToMergeInto.from) {
newIntervals.push({
from: interval.from,
to: subtractHour(valueToMergeInto.from),
value: interval.value,
}, {
from: valueToMergeInto.from,
to: Math.min(valueToMergeInto.to, interval.to),
value: interval.value,
});
if (interval.to !== valueToMergeInto.to) {
newIntervals.push({
from: addHour(Math.min(valueToMergeInto.to, interval.to)),
to: Math.max(valueToMergeInto.to, interval.to),
value: valueToMergeInto.to < interval.to ? interval.value : 0,
});
}
} else {
if (interval.from !== valueToMergeInto.from) {
newIntervals.push({
from: valueToMergeInto.from,
to: subtractHour(interval.from),
value: 0,
});
}
newIntervals.push({
from: interval.from,
to: Math.min(interval.to, valueToMergeInto.to),
value: interval.value,
});
if (interval.to !== valueToMergeInto.to) {
newIntervals.push({
from: addHour(Math.min(valueToMergeInto.to, interval.to)),
to: Math.max(valueToMergeInto.to, interval.to),
value: valueToMergeInto.to < interval.to ? interval.value : 0,
});
}
}
const valuesToMergeIntoArray = result.splice(firstIntersectionIndex, count, ...newIntervals);
valuesToMergeIntoArray.forEach(period => addInterval(interval, period));
}
}
}
}
【问题讨论】:
-
我不明白你是如何从输入到输出的,你需要解释你实际在做什么。 (最好展示您当前的解决方案,即使它不理想/功能不全)
-
我加了一张说明合并原理的图
-
图像并不能真正告诉我们什么,我们需要看到输入和输出之间的获取过程,而不仅仅是相同输入/输出数据的图形版本。由于您已经有了工作代码,请将其包含在 minimal example 中
-
@greatstone 我的第一步是根据开始时间和结束时间对输入数据进行排序。
-
您的 from/to 值是否仅限于整数或可以是任何数字(即 4.55)?
标签: javascript algorithm intervals