图的最小生成树问题
表示一张图,有点集合和边集合
树是有向无环图
关于最小生成树问题有两种算法
- 普利姆算法(加点法)
- 克鲁斯卡尔算法(加边法)
普利姆算法(加点法)
- 现任选一个点为起点
- 找到以当前选中的点为起点的最短路径的边
- 如果这个边的另一端还未联通,那么就联通它
- 如果这个边的另一端已联通,则看倒数第二短的边
- 重复2--4直到所有的点全部联通为止
var max = 100000;
// 点的集合
var pointSet = [];
// 边的集合
var distance = [
[0, 4, 7, max, max],
[4, 0, 8, 6, max],
[7, 8, 0, 5, max],
[max, 6, 5, 0, 7],
[max, max, max, 7, 0],
];
function Node(value) {
this.value = value;
this.neighbor = [];
}
var a = new Node("A");
var b = new Node("B");
var c = new Node("C");
var d = new Node("D");
var e = new Node("E");
pointSet.push(a);
pointSet.push(b);
pointSet.push(c);
pointSet.push(d);
pointSet.push(e);
function getIndex(str) {
for (var i = 0; i < pointSet.length; i++) {
if (str == pointSet[i].value) return i; //找到第几行
}
// 找不到返回-1
return -1;
}
// 获取链接距离最短的点
function getMinDisNode(pointSet, distance, alreadySet) {
// 传入点,边,以及已经链接的点
var fromNode = null; //起点
var toNode = null; //终点
var minDis = max; //距离
// 根据已有的点为起点,找其他点
for (var i = 0; i < alreadySet.length; i++) {
// 遍历起点
var nowRowIndex = getIndex(alreadySet[i].value); //记录当前行的序号
for (var j = 0; j < distance[nowRowIndex].length; j++) {
// 遍历序号对应的距离的行
var thisNode = pointSet[j]; //二维数组中当前行的当前列的点
if (
alreadySet.indexOf(thisNode) < 0 && //当前节点未链接,不存在
distance[nowRowIndex][j] < minDis //距离也是当前最短
) {
fromNode = alreadySet[i];
toNode = thisNode;
minDis = distance[nowRowIndex][j];
}
}
}
// 将起点和终点相连接
fromNode.neighbor.push(toNode);
toNode.neighbor.push(fromNode);
return toNode;
}
// 普利姆算法
function Prim(pointSet, distance, start) {
var alreadySet = [];
// 传入一个起始点
alreadySet.push(start);
//根据这个点开始找边
while (true) {
var minDisNode = getMinDisNode(pointSet, distance, alreadySet);
alreadySet.push(minDisNode);
if (alreadySet.length == pointSet.length) {
// 所有的点都连接了
break;
}
}
}
Prim(pointSet, distance, c);
console.log(pointSet);
克鲁斯卡尔算法(加边法)
- 选择最短的边进行链接,
- 保证这个边的两端至少有一个点是新的点
- 或者这个点是把两个已连接的点(组成的段)连接起来
- 重复1--3直到全部链接
var max = 100000;
// 点的集合
var pointSet = [];
// 边的集合
var distance = [
[0, 4, 7, max, max],
[4, 0, 8, 6, max],
[7, 8, 0, 5, max],
[max, 6, 5, 0, 7],
[max, max, max, 7, 0],
];
function Node(value) {
this.value = value;
this.neighbor = [];
}
var a = new Node("A");
var b = new Node("B");
var c = new Node("C");
var d = new Node("D");
var e = new Node("E");
pointSet.push(a);
pointSet.push(b);
pointSet.push(c);
pointSet.push(d);
pointSet.push(e);
// 判断是否可以连接
function canLink(resultList, tempBegin, tempEnd) {
var begin = null;
var end = null;
for (var i = 0; i < resultList.length; i++) {
if (resultList[i].indexOf(tempBegin) > -1) {
begin = resultList[i];
}
if (resultList[i].indexOf(tempEnd) > -1) {
end = resultList[i];
}
}
// 两个点都是新的--可以连接
// 一个在部落,一个不在部落里--可以连接
// 两个在不同的部落--可以连接
// 在同一个部落--不可以
if (begin != null && end != null && begin == end) {
return false;
}
return true;
}
// 连接
function link(resultList, tempBegin, tempEnd) {
var begin = null;
var end = null;
for (var i = 0; i < resultList.length; i++) {
if (resultList[i].indexOf(tempBegin) > -1) {
begin = resultList[i];
}
if (resultList[i].indexOf(tempEnd) > -1) {
end = resultList[i];
}
}
// 两个都是新的点
if (begin == null && end == null) {
var newArr = [];
newArr.push(tempBegin);
newArr.push(tempEnd);
resultList.push(newArr);
}
//begin不在部落,end在部落
else if (begin == null && end != null) {
end.push(tempBegin);
} else if (begin != null && end == null) {
begin.push(tempEnd);
} else if (
// 两个不同的部落
begin != null &&
end != null &&
begin != end
) {
// 部落合并
var allin = begin.concat(end);
// 删除它
var remove = resultList.indexOf(end);
resultList.splice(remove, 1);
var removeBegin = resultList.indexOf(begin);
resultList.splice(removeBegin, 1);
resultList.push(allin);
}
tempBegin.neighbor.push(tempEnd);
tempEnd.neighbor.push(tempBegin);
}
//克鲁斯卡尔算法
// 不用给起始点
function Kruskar(pointSet, distance) {
var resultList = []; //这是一个二维数组,用来表示有多少个部落
while (true) {
var begin = null;
var end = null;
var minDis = max;
for (var i = 0; i < distance.length; i++) {
for (var j = 0; j < distance[i].length; j++) {
// 二维数组,遍历第i行的第j个数
var tempBegin = pointSet[i];
var tempEnd = pointSet[j];
if (
i != j && //这里的值都为0
distance[i][j] < minDis &&
canLink(resultList, tempBegin, tempEnd)
) {
// 条件都满足
minDis = distance[i][j];
begin = tempBegin;
end = tempEnd;
}
}
}
link(resultList, begin, end);
if (resultList.length == 1 && resultList[0].length == pointSet.length) {
break;
}
}
}
Kruskar(pointSet, distance);
console.log(pointSet);