【算法】图的最小生成树

211 阅读2分钟

把构造连通网的最小代价生成树称为最小生成树。经典的算法有两种,普利姆算法和克鲁斯卡尔算法。

一、普利姆算法 (Prim算法)

先选择其中一个点,把该顶点的边加入数组。

再按照权值最小的原则选边,选完最小权值的边,把所选边的另一顶点的边加入数组。

再选权值最小的边,如此循环(有多少顶点循环多少次)。

截屏2022-05-21 下午3.54.45.png

function Node(value) {
    this.value = value;
    this.neighbor = [];
    this.distance = [];
}
const a = new Node("A");
const b = new Node("B");
const c = new Node("C");
const d = new Node("D");
const e = new Node("E");

let max = Number.POSITIVE_INFINITY; //无穷大;
let pointSet = [a, b, c, d, e];
let distance = [
    [0, 3, max, 2, max],
    [3, 0, 5, max, max],
    [max, 5, 0, 7, 1],
    [2, max, 7, 0, 2],
    [max, max, 1, 2, 0]
];


function getMinPoint(pointSet, distance, nowPointSet) {
    let fromNode = null;//起始节点
    let endNode = null;//终止节点
    let minDistance = max;//最小距离默认为max

    for(let i = 0; i < nowPointSet.length; i ++) { //遍历已连接的点

        let nowPointIndex = getIndex(nowPointSet[i].value); //获取已连接节点在pointSet中的索引值 拿到序号
        let pointDistance = distance[nowPointIndex]; //通过nowPointIndex找到该连接节点对应所有边的开销

        for(let j = 0; j < pointDistance.length; j ++) { //遍历所有边的开销 遍历一整行

            let thisNode = pointSet[j]; //行里的每个节点

            if (nowPointSet.indexOf(thisNode) < 0 //最小距离连接的节点不能在nowPointSet中 && 要小于minDistance
            && pointDistance[j] < minDistance) {

                fromNode = nowPointSet[i];
                endNode = thisNode;
                minDistance = pointDistance[j];

            }		 

        }
    }

    fromNode.neighbor.push(endNode);//起始节点 将开销最小的节点加入
    fromNode.distance.push({//起始节点 将开销最小的节点的值和距离加入
        from: fromNode.value,
        to: endNode.value,
        distance: minDistance
    });
    endNode.neighbor.push(fromNode);
    endNode.distance.push({
        from: fromNode.value,
        to: endNode.value,
        distance: minDistance
    });

    return endNode;//返回开销最小的节点

}

function getIndex(str) {
    for (var i = 0; i < pointSet.length; i++) {
        if (str == pointSet[i].value) {
            return i;
        }
    }
    return -1;
}


function prim(pointSet, distance, start) {
    let nowPointSet = [];
    nowPointSet.push(start); //将开始节点放入已连接数组中
    while(true) {
        let minPoint = getMinPoint(pointSet, distance, nowPointSet); //通过已连接节点,找到和它们相连接开销最小的节点
        nowPointSet.push(minPoint); //将开销最小的节点加入已连接数组中
        if(pointSet.length == nowPointSet.length) break; //所有节点都连接,跳出循环
    }
    console.log(nowPointSet);  
}
prim(pointSet, distance, pointSet[2]);


// [
// 	Node { value: 'C', neighbor: [ [Node] ], distance: [ [Object] ] },
// 	Node {
// 		value: 'E',
// 		neighbor: [ [Node], [Node] ],
// 		distance: [ [Object], [Object] ]
// 	},
// 	Node {
// 		value: 'D',
// 		neighbor: [ [Node], [Node] ],
// 		distance: [ [Object], [Object] ]
// 	},
// 	Node {
// 		value: 'A',
// 		neighbor: [ [Node], [Node] ],
// 		distance: [ [Object], [Object] ]
// 	},
// 	Node { value: 'B', neighbor: [ [Node] ], distance: [ [Object] ] }
// ]

二、克鲁斯卡尔算法