Arrays 分割重叠间隔,然后合并重复项的更有效方法

Arrays 分割重叠间隔,然后合并重复项的更有效方法,arrays,typescript,intervals,Arrays,Typescript,Intervals,因此,我试图找出最有效的方法来分割重叠的间隔,然后合并重复项。针对我的情况,有两个条件是,如果合并间隔的开始是原始间隔的结束,则它将递增1。如果合并间隔的结束是原始间隔的开始,则其递减1。以下是一些示例数据和预期结果: interface Interval { start: number; end: number; type: Array<number>; } // starting data const arr: Array<Interval>

因此,我试图找出最有效的方法来分割重叠的间隔,然后合并重复项。针对我的情况,有两个条件是,如果合并间隔的开始是原始间隔的结束,则它将递增1。如果合并间隔的结束是原始间隔的开始,则其递减1。以下是一些示例数据和预期结果:

interface Interval {
    start: number;
    end: number;
    type: Array<number>;
}

// starting data
const arr: Array<Interval> = [
    { start: 0, end: 16, type: [42] },
    { start: 6, end: 30, type: [95] },
    { start: 11, end: 24, type: [126] },
    { start: 32, end: 47, type: [42] }
].sort((a, b) => a.start - b.start);

// magic splitting code here

// what we expect to end up with
const end_arr: Array<Interval> = [
    { start: 0, end: 5, type: [42] },
    { start: 6, end: 10, type: [42, 95] },
    { start: 11, end: 16, type: [42, 95, 126] },
    { start: 17, end: 24, type: [95, 126] },
    { start: 25, end: 30, type: [95] },
    { start: 32, end: 47, type: [42] },
];
接口间隔{
开始:编号;
完:编号;;
类型:阵列;
}
//起始数据
常量arr:数组=[
{开始:0,结束:16,类型:[42]},
{开始:6,结束:30,类型:[95]},
{开始:11,结束:24,类型:[126]},
{开始:32,结束:47,类型:[42]}
].sort((a,b)=>a.start-b.start);
//神奇的分裂代码在这里
//我们期望的结果是什么
常量结束数组:数组=[
{开始:0,结束:5,类型:[42]},
{开始:6,结束:10,键入:[42,95]},
{开始:11,结束:16,键入:[42,95,126]},
{开始:17,结束:24,键入:[95126]},
{开始:25,结束:30,类型:[95]},
{开始:32,结束:47,类型:[42]},
];
我已经从技术上得到了这个问题的答案,但它不是很有效——包括3个嵌套的for/forEach循环。肯定有更有效的方法吗?以下是代码:

let startIndexArray: Array<number> = [];

let endIndexArray: Array<number> = [];

for (let i = 0; i < arr.length; i++) {
    startIndexArray.push(arr[i].start);
    endIndexArray.push(arr[i].end);
}

startIndexArray = startIndexArray.sort((a, b) => a - b);
endIndexArray = endIndexArray.sort((a, b) => a - b);

const indexArray = [...startIndexArray, ...endIndexArray].sort((a, b) => a - b);

const result: Array<Interval> = [];

arr.forEach((currentInterval) => {
    for (let i = currentInterval.start; i < currentInterval.end; i++) {
        if (indexArray.includes(i)) {
            const position = indexArray.indexOf(i);

            if (position !== indexArray.length - 1) {
                let start = i;
                let next = indexArray[position + 1];

                if (endIndexArray.includes(start)) {
                    start = start + 1;
                }

                if (startIndexArray.includes(next)) {
                    next = next - 1;
                }

                let in_result = false;
                result.forEach((mergedInterval) => {
                    if (mergedInterval.start === start && mergedInterval.end === next) {
                        mergedInterval.type = [...mergedInterval.type, ...currentInterval.type];
                        in_result = true;
                    }
                });
                if (!in_result) {
                    result.push({ start: start, end: next, type: [...currentInterval.type]});
                }
            }
        }
    }
});

// output is my expected, correct outcome
console.log(result);
让startIndexArray:Array=[];
让endIndexArray:Array=[];
for(设i=0;ia-b);
endIndexArray=endIndexArray.sort((a,b)=>a-b);
常量indexArray=[…startIndexArray,…endIndexArray].sort((a,b)=>a-b);
常量结果:数组=[];
arr.forEach((当前间隔)=>{
对于(设i=currentInterval.start;i{
if(mergedInterval.start==start&&mergedInterval.end==next){
mergedInterval.type=[…mergedInterval.type,…currentInterval.type];
in_result=true;
}
});
如果(!in_结果){
push({start:start,end:next,type:[…currentInterval.type]});
}
}
}
}
});
//输出是我期望的、正确的结果
控制台日志(结果);

以下算法是我能想到的最干净的算法,具有合理的性能。我希望,对于您给出的特定示例数组,这段代码和您上面给出的代码将具有类似的性能级别,但是如果您开始使用更大的数组,您将看到与您的数组相比,这里的性能有所提高。没有一套测试用例,很难确定

总之,总的想法是这样的。让我们调用
分区
一个包含从
-Infinity
Infinity
的所有整数的非重叠区间排序数组

type Partition = Array<Interval>;

这个算法是一个二进制搜索,如果您碰巧已经知道分区中正确的间隔可能在哪里,它允许您为它提供关于开始和结束索引的提示。如果分区长度很长,那么对于您的用例来说,效率是一个实际的问题吗?数组是否可能包含跨越数千个时间步的数千个间隔?我之所以这样问,是因为正确性通常比性能更重要,这是一个“幼稚”的实现,如图所示,将间隔数组展开为一系列时间步,然后将它们重新回滚到间隔,与建议的更有效的堆栈溢出版本相比,该算法的性能可能更好,并且不太可能出现奇怪的边缘情况。@jcalz此算法作为一个更大进程的一部分被调用,该进程可能调用数百或数千条数据,然后需要尽快向用户提供这些数据-因此这一步骤应该是有效的。您链接的解决方案比我的原始代码慢。您能指定确切的输入输出关系吗?
start
end
是否始终为非负整数?如果同一
类型的输入间隔重叠或相遇,会发生什么情况?e、 例如,
[{start:0,end:10,type:[42]},{start:5,end:12,type:[42]}]
[{start:0,end:10,type:[42]},{start:11,end:12,type:[42]}我希望更快一些;但不确定边缘情况。让我知道这是否对你有效,我会写下来作为答案。否则,祝你好运!开始和结束总是非负的,相同类型的输入间隔永远不会重叠。您的新解决方案确实比我的原始代码快3-4倍,谢谢!在对我的示例输入进行测试时,我没有发现任何边缘情况,因此它看起来工作得很好。感谢您的帮助,尽管我注意到您提出的第二个解决方案实际上比这个解决方案性能更好-我想这是性能与正确性的对比
// binary search of the partition to find the index of the interval containing position
// startIndex is a hint, where partition[startIndex].start <= position
// endIndex is a hint, where partition[startIndex].end > position
function findIndex(
    partition: Partition,
    position: number,
    startIndex: number = 0,
    endIndex: number = partition.length
) {
    while (true) {
        let i = (startIndex + endIndex) >> 1;
        let cur = partition[i];
        if (cur.end <= position) {
            startIndex = i;
        } else if (cur.start > position) {
            endIndex = i;
        } else {
            return i;
        }
    }
}