You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求合并简化重合折线的经典算法或可用JavaScript实现

线段合并为折线集合的高效算法与JS实现

问题说明

我需要实现从线段列表生成折线集合的功能,核心目标是尽可能延长每条折线。现有一个Moly多折线组类,支持通过Add方法添加折线组,调用reduce方法完成折线合并。

示例输入输出

输入线段列表:

segments=[
 ['AxAy','BxBy'],
 ['BxBy','CxCy'],
 ...
 ['LxLy','MxMy'],
 ['NxNy','MxMy'],

 ['QxQy','RxRy'],
 ['RxRy','SxSy']
]

使用方式:

var moly= new Moly([segments.shift()])
segments.map(segment=>moly.Add(new Moly(segment)))
moly.reduce()

输出合并后的折线:

[
  ['AxAy','BxBy','CxCy',...,'LxLy','MxMy','NxNy'],
  ['QxQy','RxRy','SxSy']
]

当前代码能生成可用结果,但因调用频次极高,需要找到该经典问题的对应算法,或更高效的现成JavaScript实现。

我正在测试的代码

function Poly(polylines){
    this.polylines=[]
    for (i=0;i<polylines.length;i++)
        this.Add(polylines[i])
    return this
}

Poly.prototype.Add=function (polyline){
    this.polylines.push(polyline.map(JSON.stringify))
}

Poly.prototype.Reduce=function(){
    for (var i=0;i<this.polylines.length;i++){
        this.polylines=this.polylines.sort((a,b)=>b.length - a.length)
        for (var j=i+1;j<this.polylines.length;j++)
            this.Improve(i,j)
    }
    this.polylines=this.polylines.filter(p=>p.length>0)
}

Poly.prototype.Draw=function(){
    return this.polylines.map(polyline=>polyline.map(JSON.parse))
}

Poly.prototype.Improve=function(I,J){
    var a=this.polylines[I]
    var b=this.polylines[J]
    for (var i=0;i<a.length;i++)
        if (a[i]===b[0] || a[i]===b[b.length-1]){
            if (a[i]===b[b.length-1])
                b.reverse()
            if (i===0)
                a.reverse()
            if (i===0 || i===a.length-1){
                a.pop()
                this.polylines[I]=a.concat(b)
                this.polylines[J]=[]
                return true
            }

            if (i-1+b.length > a.length){
                this.polylines[I]=a.slice(0,i-1).concat(b)
                this.polylines[J]=a.slice(i)
                return true
            }
            if (a.length-i-1+b.length > a.length){
                this.polylines[I]=b.reverse().concat(a.slice(i+1))
                this.polylines[J]=a.slice(0,i+1)
                return true
            }  
        }
    return false
}
                
module.exports=Poly

对应经典算法与优化实现

这个问题本质是无向图的路径合并:每个线段是图中的边,坐标点是图的节点,我们需要找出所有的欧拉路径片段。高效实现的核心是用哈希表记录每个节点的邻接折线端点,避免暴力遍历,大幅提升合并效率。

优化后的JavaScript实现

class PolylineMerger {
    constructor(segments = []) {
        this.polylines = [];
        // 哈希表:节点 -> 折线的端点索引(0=起点,1=终点)及折线在polylines中的位置
        this.nodeMap = new Map();
        segments.forEach(seg => this.addSegment(seg));
    }

    // 添加单条线段
    addSegment(segment) {
        const [start, end] = segment.map(JSON.stringify);
        const polyline = [start, end];
        this.polylines.push(polyline);
        const idx = this.polylines.length - 1;
        // 更新节点映射
        this._updateNodeMap(start, idx, 0);
        this._updateNodeMap(end, idx, 1);
    }

    // 添加折线组
    addPolyline(polyline) {
        const strPoly = polyline.map(JSON.stringify);
        this.polylines.push(strPoly);
        const idx = this.polylines.length - 1;
        this._updateNodeMap(strPoly[0], idx, 0);
        this._updateNodeMap(strPoly.at(-1), idx, 1);
    }

    _updateNodeMap(node, polyIdx, endType) {
        if (!this.nodeMap.has(node)) {
            this.nodeMap.set(node, []);
        }
        this.nodeMap.get(node).push({ polyIdx, endType });
    }

    // 合并折线
    reduce() {
        // 遍历所有节点,处理有连接关系的折线
        for (const [node, connections] of this.nodeMap) {
            while (connections.length >= 2) {
                const conn1 = connections.shift();
                const conn2 = connections.shift();
                if (conn1.polyIdx === conn2.polyIdx) continue;

                const poly1 = this.polylines[conn1.polyIdx];
                const poly2 = this.polylines[conn2.polyIdx];
                if (!poly1 || !poly2) continue;

                let merged;
                // 处理不同的连接情况
                if (conn1.endType === 0 && conn2.endType === 0) {
                    // poly1起点连poly2起点,反转poly2后拼接
                    merged = poly2.reverse().concat(poly1.slice(1));
                } else if (conn1.endType === 0 && conn2.endType === 1) {
                    // poly1起点连poly2终点,直接拼接poly1到poly2
                    merged = poly2.concat(poly1.slice(1));
                } else if (conn1.endType === 1 && conn2.endType === 0) {
                    // poly1终点连poly2起点,直接拼接
                    merged = poly1.concat(poly2.slice(1));
                } else {
                    // poly1终点连poly2终点,反转poly2后拼接
                    merged = poly1.concat(poly2.reverse().slice(1));
                }

                // 更新polylines:标记原折线为null,添加新折线
                this.polylines[conn1.polyIdx] = null;
                this.polylines[conn2.polyIdx] = null;
                this.polylines.push(merged);
                const newIdx = this.polylines.length - 1;

                // 更新节点映射:移除原折线的端点,添加新折线的端点
                this._removeNodeConnections(conn1.polyIdx);
                this._removeNodeConnections(conn2.polyIdx);
                this._updateNodeMap(merged[0], newIdx, 0);
                this._updateNodeMap(merged.at(-1), newIdx, 1);

                // 将新折线的端点连接放回队列,继续合并
                const startConn = this.nodeMap.get(merged[0]);
                startConn.push({ polyIdx: newIdx, endType: 0 });
                const endConn = this.nodeMap.get(merged.at(-1));
                endConn.push({ polyIdx: newIdx, endType: 1 });
            }
        }

        // 过滤掉已合并的空折线,解析回原始格式
        this.polylines = this.polylines.filter(p => p !== null).map(p => p.map(JSON.parse));
    }

    _removeNodeConnections(polyIdx) {
        const poly = this.polylines[polyIdx];
        if (!poly) return;
        const start = poly[0];
        const end = poly.at(-1);
        // 移除该折线在节点映射中的记录
        this.nodeMap.set(start, this.nodeMap.get(start).filter(c => c.polyIdx !== polyIdx));
        this.nodeMap.set(end, this.nodeMap.get(end).filter(c => c.polyIdx !== polyIdx));
    }
}

// 使用示例
// const segments = [['AxAy','BxBy'], ['BxBy','CxCy'], ['NxNy','MxMy'], ['LxLy','MxMy'], ['QxQy','RxRy'], ['RxRy','SxSy']];
// const merger = new PolylineMerger(segments);
// merger.reduce();
// console.log(merger.polylines);

优化点说明

  • 使用**哈希表(Map)**记录节点与折线端点的关联,避免暴力遍历所有折线对,时间复杂度从O(n²)优化为接近O(n)
  • 合并时直接处理节点的连接关系,无需重复排序,减少不必要的计算
  • 合并后及时更新节点映射,确保后续合并能正确找到可连接的折线

内容的提问来源于stack exchange,提问作者Mark Lester

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:02:03