1

如果我有以下数组,每个元素代表整数范围对:

var a = [[0, 47], [50, 51], [53, 53], [55, 55], [58, 97], [101, 101], [103, 1114111]];
var b = [[48, 57], [97, 102]];

我正在使用此代码计算交集

var output = [];

for (var i = 0; i < a.length; ++i) {
    for (var j = 0; j < b.length; ++j) {
        var intersection = [
        Math.max(a[i][0], b[j][0]),
        Math.min(a[i][1], b[j][1]), ];

        if (intersection[0] <= intersection[1]) {
            output.push(intersection)
        }
    }
}

console.log(JSON.stringify(output));

[ [ 50, 51 ], [ 53, 53 ], [ 55, 55 ], [ 97, 97 ], [ 101, 101 ] ]

我还需要计算差异(所有值 0..1114111 除了上面相交的范围)。

这样做的有效方法是什么?

4

1 回答 1

3

观察:“除了上面相交的范围之外的所有值 0..1114111”实际上是微不足道的,您只需遍历交叉点并输出其补码(将端点连接到以下间隔的起点)。

所以你的问题减少到更快地找到交叉点。使用扫描线算法:

  1. 创建一个事件列表,(t, x, y)其中t是区间的边界点,x如果区间来自,则为 1,如果来自a,则为 2 by如果边界点是起点,则为 1;如果边界点为终点,则为 -1。
  2. 按字典顺序排序(t, -y)
  3. count[1] = count[2] = 0
  4. 遍历事件点。更新count[x] += y

count[1] > 0现在结果是同时在哪里的范围count[2] > 0

复杂度是O(n log n)。这是一个代码示例: http: //jsfiddle.net/QA5FY/14/感谢用户 Xotic750 提供基本实现。

Javascript

var a = [
    [0, 47],
    [50, 51],
    [53, 53],
    [55, 55],
    [58, 97],
    [101, 101],
    [103, 1114111]
];

var b = [
    [48, 57],
    [97, 102]
];

var evts = [];

function add(arr, x) {
    arr.forEach(function (pair) {
        evts.push({
            t: pair[0],
            x: x,
            y: 1
        }, {
            t: pair[1],
            x: x,
            y: -1
        });
    });
}

add(a, 0);
add(b, 1);

evts.sort(function (a, b) {
    return (a.t != b.t) ? (a.t - b.t) : (b.y - a.y);
});

var last = -1;
var count = [0, 0];
var res = [];

for (var i = 0; i < evts.length; ++i) {
    count[evts[i].x] += evts[i].y;
    if (count[evts[i].x] === 1 && count[evts[i].x ^ 1] > 0) last = i;
    else if (count[0] === 0 || count[1] === 0) {
        if (last >= 0) res.push([evts[last].t, evts[i].t]);
        last = -1;
    }
}

res.forEach(function (pair) {
    console.log(pair[0] + " " + pair[1]);
});

输出

50 51
53 53
55 55
97 97
101 101 
于 2014-02-11T15:04:21.837 回答