最小生成树之Prim普里姆算法

| 发布     | 分类 算法  | 标签 算法 



相关链接

783. 最小危险值路径
数据结构–最小生成树详解
最短路径算法之 Floyd

在线测试

寻路算法 在线测试

1. 点击右边的测试数据按钮,可以切换数据


2. 点击右边的最小生成树,可以切换是否开启这功能


3. 点击右边的操作模式
* 拖动节点: 可以拖动节点,默认都可以 * 设置起始点: 选中该模式,再点击节点,可以将该节点设置为最短寻路起始点。节点下标Start
* 设置结束点: 选中该模式,再点击节点,可以将该节点设置为最短寻结束点。节点下标End
寻得的路线为绿色连线,红色表示该路线中成本最大的值


4. 修改数据
(1) 先点击“导出数据按钮”,会在页面下方输出图形json数据
(2) 编辑数据,点提交就可以修改了。只需要修改属性 x(起点), y(目标点), w(成本)


Prim.ts

namespace ihaiu
{
    /** 作为记录边的信息,这些边都是达到end的所有边中,权重最小的那个 */
    export class PrimAssis
    {
        // 边的终点
        start: number = 0;
        // 边的起点
        end: number = 0;
        // 边的权重
        weight: number = 0;
    }

    /** 最小生成树之Prim普里姆算法 */
    export class Prim
    {

        // 成本矩阵
        arcs: number[][];

        // 计算结果
        result: GraphData;

        constructor()
        {

        }


        /** 获取路径成本 */
        public getArcs(from: number, to: number): number
        {
            return this.arcs[from][to];
        }


        /** 是否有连接线 */
        public hasEdge(from: number, to: number): boolean
        {
            let arc = this.getArcs(from, to);
            return arc != Number.MAX_VALUE && arc != 0;
        }

        public calculationByGraphData(g: GraphData, beginIndex: number = 0):GraphData
        {
            g.check();
            let g2 = this.calculation(
                            g.nodeNum, 
                            g.edegeNum,
                            g.x,
                            g.y,
                            g.w,
                            beginIndex
                            );

            g2.nodeNum = g.nodeNum;             
            g2.nodes = g.nodes;
            return g2;
        }


        /** 计算寻路 */
        public calculation(nodeNum: number, edegeNum: number, x: number[], y: number[], w: number[], beginIndex: number = 0):GraphData
        {
            // 结果图
            let result: GraphData = new GraphData().init();

            // 成本矩阵
            let arcs: number[][] = [];


            // 初始化矩阵值
            for(let u = 0; u < nodeNum; u ++)
            {
                arcs[u] = [];

                for(let v = 0; v < nodeNum; v ++)
                {
                    arcs[u][v] = Number.MAX_VALUE;
                }
            }


            // 输入边
            for(let i = 0; i < x.length; i ++)
            {
                let u = x[i];
                let v = y[i];
                arcs[u][v] = w[i];
                arcs[v][u] = w[i];
            }

            //closeEdge这个数组记录到达某个顶点的各个边中的权重最大的那个边
            let closeEdges: PrimAssis[] = [];
            for(let i = 0; i < nodeNum; i ++)
            {
                closeEdges.push(new PrimAssis());
            }

            //进行closeEdge的初始化,更加开始起点进行初始化
            for(let i = 0; i < nodeNum; i ++)
            {
                if(i != beginIndex)
                {
                    let edge = closeEdges[i];
                    edge.start = beginIndex;
                    edge.end = i;
                    edge.weight = arcs[beginIndex][i];
                }
            }

            //把起点的closeEdge中的值设置为-1,代表已经加入到集合U了
            closeEdges[beginIndex].weight = -1;

             //访问剩下的顶点,并加入依次加入到集合U
             for(let i = 1; i< nodeNum; i ++)
             {
                let minWeight = Number.MAX_VALUE;
                let minIndex = 0;
                //寻找数组close_edge中权重最小的那个边
                for(let k = 0; k < nodeNum; k ++)
                {
                    if(closeEdges[k].weight != -1)
                    {
                        if(closeEdges[k].weight < minWeight)
                        {
                            minWeight = closeEdges[k].weight;
                            minIndex = k;
                        }
                    }
                }

                let minEdge = closeEdges[minIndex];

                //将权重最小的那条边的终点也加入到集合U
                minEdge.weight = -1;


                //输出对应的边的信息
                result.addEdge(
                    minEdge.start, 
                    minEdge.end, 
                    arcs[minEdge.start][minEdge.end]
                    );

                    //更新我们的close_edge数组。
                    for (let k = 0; k < nodeNum; k++) {
                        let edge = closeEdges[k];
                        if (arcs[minEdge.end][k] < edge.weight) 
                        {
                            edge.weight = arcs[minEdge.end][k];
                            edge.start = minEdge.end;
                            edge.end = k;
                        }
                    }


             }

             this.arcs = arcs;
             this.result = result;
             return result;
        }







    }
}

GraphData.ts

namespace ihaiu
{
    /**
     * 图形数据
     */
    export class GraphData
    {
        // 边--from
        x: number[];
        // 边--to
        y: number[];
        // 边--危险值
        w: number[];

        // 节点数量
        nodeNum = 0;

        // 边数
        edegeNum = 0;

        // 节点
        nodes: Node[] = [];

        public init(): GraphData
        {
            this.x = [];
            this.y = [];
            this.w = [];
            return this;
        }

        // 添加边
        public addEdge(x: number, y:number, w: number): GraphData
        {
            this.x.push(x);
            this.y.push(y);
            this.w.push(w);
            return this;
        }

        public check()
        {
            this.checkNodeNum();
            this.edegeNum = this.x.length;
            if(this.nodes != null && this.nodes.length >= this.nodeNum)
            {
                return;
            }

            let list: Node[] = [];
            for(let i = 0; i < this.nodeNum; i ++)
            {

                let node : Node;
                
                if(i < this.nodes.length )
                {
                    node = this.nodes[i];
                } 
                else
                {
                    node = new Node();
                    node.pos.x = Math.floor( Math.random() * 200 + 50 ) ;
                    node.pos.y = Math.floor( Math.random() * 200 + 50 );

                }
                node.index = i;
                list.push(node);
            }
            this.nodes = list;
        }

        // 矫正顶点数量
        public checkNodeNum()
        {
            let x = this.x;
            let y = this.y;
            let num = 0;
            for(let i = 0; i < x.length; i ++)
            {  
                num = Math.max(x[i], num);
                num = Math.max(y[i], num);
            }
            this.nodeNum = Math.max(num + 1, this.nodeNum);
        }

        static parse(json:string)
        {
            let g = new GraphData();
            try 
            {
                let o = JSON.parse(json);
                g.edegeNum = o.edegeNum;
                g.nodeNum = o.nodeNum;
                g.x = o.x;
                g.y = o.y;
                g.w = o.w;

                let list: Node[] = [];
                for(let i = 0; i < o.nodes.length; i ++)
                {
                    let item = o.nodes[i];

                    let node : Node = new Node();
                    node.index = item.index;
                    node.pos.x = item.pos.x;
                    node.pos.y = item.pos.y;
                    list.push(node);
                }
                g.nodes = list;

            } 
            catch (error) 
            {
                alert("解析json数据错误:" + error);
            }
            return g;
        }


    }
}

Node.ts

namespace ihaiu
{
    export class NodeState
    {
        static Normal = 0;
        static Disable = 1;
        static Start = 2;
        static End = 3;
        static Greend = 4;
    }

    export class LineState
    {
        static Normal = 0;
        static Disable = 1;
        static Green = 2;
        static GreenMax = 3;
    }

    /**
     * 节点
     */
    export class Node
    {
        // 节点索引
        index: number;
        // 坐标
        pos: Point = new Point();
        // 子节点
        // childes: Node[] = [];
    }
}

Point.ts

namespace ihaiu
{
    /**
     * 路径点
     */
    export class Point
    {
        x: number = 0;
        y: number = 0;

        /** 获取到目标点距离 */
        public getDistance(to: Point): number
        {
            let dx  = to.x - this.x;
            let dy  = to.y - this.y;
            return Math.sqrt(dx * dx + dy * dy);
        }

        /** 获取到目标点弧度 */
        public getRadian(to: Point) : number
        {
            let dx  = to.x - this.x;
            let dy  = to.y - this.y;
            return Math.atan2(dy, dx);
        }

        /** 获取到目标点角度 */
        public getAngle(to: Point) : number
        {
            return this.getRadian(to) * 180 / Math.PI ;
        }

        /** 获取中心点 */
        public static center(a: Point, b: Point): Point
        {
            let p = new Point();
            p.x = a.x + (b.x - a.x) * 0.5;
            p.y = a.y + (b.y - a.y) * 0.5;
            return p;
        }


        /** 获取相对A中心点 */
        public static centerRelative(a: Point, b: Point): Point
        {
            let p = new Point();
            p.x = (b.x - a.x) * 0.5;
            p.y = (b.y - a.y) * 0.5;
            return p;
        }

    }
}
上一篇: 最短路径算法之 Floyd
下一篇: Unlock 解锁文件占用