You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
4.6 KiB
JavaScript

/**
* Prim's algorithm is a greedy algorithm that finds a minimum
* spanning tree for a connected weighted undirected graph.
*
* @example
*
* var Prim = require('path-to-algorithms/src/graphs/spanning-trees/prim');
* var Graph = Prim.Graph;
* var Edge = Prim.Edge;
* var Vertex = Prim.Vertex;
*
* var graph, edges = [];
* edges.push(new Edge(new Vertex(0), new Vertex(1), 4));
* edges.push(new Edge(new Vertex(0), new Vertex(7), 8));
* edges.push(new Edge(new Vertex(1), new Vertex(7), 11));
* edges.push(new Edge(new Vertex(1), new Vertex(2), 8));
* edges.push(new Edge(new Vertex(2), new Vertex(8), 2));
* edges.push(new Edge(new Vertex(2), new Vertex(3), 7));
* edges.push(new Edge(new Vertex(2), new Vertex(5), 4));
* edges.push(new Edge(new Vertex(2), new Vertex(3), 7));
* edges.push(new Edge(new Vertex(3), new Vertex(4), 9));
* edges.push(new Edge(new Vertex(3), new Vertex(5), 14));
* edges.push(new Edge(new Vertex(4), new Vertex(5), 10));
* edges.push(new Edge(new Vertex(5), new Vertex(6), 2));
* edges.push(new Edge(new Vertex(6), new Vertex(8), 6));
* edges.push(new Edge(new Vertex(8), new Vertex(7), 7));
* graph = new Graph(edges, edges.length);
*
* // { edges:
* // [ { e: '1', v: 0, distance: 4 },
* // { e: '2', v: 8, distance: 2 },
* // { e: '3', v: 2, distance: 7 },
* // { e: '4', v: 3, distance: 9 },
* // { e: '5', v: 2, distance: 4 },
* // { e: '6', v: 5, distance: 2 },
* // { e: '7', v: 0, distance: 8 },
* // { e: '8', v: 7, distance: 7 } ],
* // nodesCount: 0 }
* var spanningTree = graph.prim();
*
* @module graphs/spanning-trees/prim
*/
(function (exports) {
'use strict';
var Heap = require('../../data-structures/heap').Heap;
exports.Vertex = require('../../data-structures/vertex').Vertex;
exports.Edge = require('../../data-structures/edge').Edge;
/**
* Graph.
*
* @constructor
* @public
* @param {Array} edges Array with graph edges.
* @param {Number} nodesCount Number of nodes in graph.
*/
exports.Graph = function (edges, nodesCount) {
this.edges = edges || [];
this.nodesCount = nodesCount || 0;
};
/**
* Executes Prim's algorithm and returns minimum spanning tree.
*
* @public
* @method
* @return {Graph} Graph which is the minimum spanning tree.
*/
exports.Graph.prototype.prim = (function () {
var queue;
/**
* Used for comparitions in the heap
*
* @private
* @param {Vertex} a First operand of the comparition.
* @param {Vertex} b Second operand of the comparition.
* @return {number} Number which which is equal, greater or
* less then zero and indicates whether the first vertex is
* "greater" than the second.
*/
function compareEdges(a, b) {
return b.distance - a.distance;
}
/**
* Initialize the algorithm.
*
* @private
*/
function init() {
queue = new Heap(compareEdges);
}
return function () {
init.call(this);
var inTheTree = {};
var startVertex = this.edges[0].e.id;
var spannigTree = [];
var parents = {};
var distances = {};
var current;
inTheTree[startVertex] = true;
queue.add({
node: startVertex,
distance: 0
});
for (var i = 0; i < this.nodesCount - 1; i += 1) {
current = queue.extract().node;
inTheTree[current] = true;
this.edges.forEach(function (e) {
if (inTheTree[e.v.id] && inTheTree[e.e.id]) {
return;
}
var collection = queue.getCollection();
var node;
if (e.e.id === current) {
node = e.v.id;
} else if (e.v.id === current) {
node = e.e.id;
} else {
return;
}
for (var i = 0; i < collection.length; i += 1) {
if (collection[i].node === node) {
if (collection[i].distance > e.distance) {
queue.changeKey(i, {
node: node,
distance: e.distance
});
parents[node] = current;
distances[node] = e.distance;
}
return;
}
}
queue.add({
node: node,
distance: e.distance
});
parents[node] = current;
distances[node] = e.distance;
});
}
for (var node in parents) {
spannigTree.push(
new exports.Edge(node, parents[node], distances[node]));
}
return new exports.Graph(spannigTree);
};
}());
})(typeof window === 'undefined' ? module.exports : window);