使用 JavaScript 基于邻接矩阵实现了图的深度、广度遍历,以及 Floyd、Dijkstra 算法求解最短路径。
另外使用 SVG 实现图的遍历可视化。
一、输入
首先,输入数据主要有两个,一个是存放节点名的数组,另一个是存放边对象的数组。
例如:
//存放图结点的数组
var graphVertex = ["v0","v1","v2","v3","v4","v5","v6"];
//存放图边的数组
var graphAdge = [
{
start:"v0",
end: "v2",
weight: 2
},
{
start:"v0",
end: "v5",
weight: 8
},
{
start:"v3",
end: "v2",
weight: 1
},
{
start:"v3",
end: "v5",
weight: 3
},
{
start:"v4",
end: "v1",
weight: 6
},
{
start:"v5",
end: "v4",
weight: 8
},
{
start:"v1",
end: "v2",
weight: 8
},
{
start:"v1",
end: "v6",
weight: 8
}
];
二、SVG画图
使用SVG画出圆形结点、节点名、边、边的权值数字以及表示有向图的箭头。
//显示图
var showGraph = function(){
var svg = document.getElementById("svg");
var circleStr = "",lineStr = "",textStr = "",arrowStr="";//圆、线和文本的HTML字符串
var adge = JSON.parse(JSON.stringify(graphAdge));//层序遍历生成数组
var vertex = JSON.parse(JSON.stringify(graphVertex));//层序遍历生成数组
var vertexObj = {};//存放所有顶点的对象,键为顶点名,值为含有属性的对象
var width = Number(svg.getAttribute("width"))-50;//画布宽度
var r = width/2;//半径
//画圆和顶点名
for(let i=0;i<vertex.length;i++){
let vertexName = vertex[i], len = vertexName.length;
let angle = (i*2*Math.PI/vertex.length);
let cx = 0, cy = 0;//当前结点的定位像素坐标
cx = r*(1 + Math.sin(angle) )+25;
cy = r*(1 - Math.cos(angle) )+25;
let obj = {
"cx":cx,
"cy":cy
};
vertexObj[vertexName] = obj;
circleStr += '<circle cx="'+cx+'" cy="'+cy+'" r="20" fill="#9F79EE"/></circle>';
//调整文本缩进
let textcx = len>1?(cx-10):(cx-5);
let textcy = cy+6;
textStr += '<text x="'+textcx+'" y="'+textcy+'" fill="black">'+vertexName+'</text>';
}
//画线和数字
for(let i =0;i<adge.length;i++){
//如果依然处于当前层,则累加占用宽度,否则将占用宽度置零,更新层数
let startcx = 0, startcy = 0,endcx = 0,endcy = 0;//当前结点的定位像素坐标
if(!vertexObj[adge[i].start]||!vertexObj[adge[i].end]){
throw Error("边数组中有结点不在结点数组中");
return;
}
startcx = vertexObj[adge[i].start].cx;
startcy = vertexObj[adge[i].start].cy;
endcx = vertexObj[adge[i].end].cx;
endcy = vertexObj[adge[i].end].cy;
lineStr += '<line x1="'+startcx+'" y1="'+startcy+'" x2="'+endcx+
'" y2="'+endcy+'" style="stroke:#999;stroke-width:2" />';
//计算三角形箭头的坐标和旋转角
var getTriangle = function(obj){
var x = endcx,y=endcy+20;
startcx = obj.startcx;
startcy= obj.startcy;
endcx= obj.endcx;
endcy= obj.endcy;
var x = endcx, y = endcy+20;
var points = [x-7,y+15,x+7,y+15,x,y].join(",");//一个三角形三点的坐标
var angle = 0;
//注意这里的坐标系和通常情况下的坐标系y轴是相反的,因此 endcy-startcy 要变为startcy-endcy
angle =180*Math.acos((startcy-endcy)/Math.sqrt((endcx-startcx)*(endcx-startcx)+
(startcy-endcy)*(startcy-endcy)))/Math.PI;
if(endcx-startcx < 0){
angle = 360 - angle;
}
var out = {
"points":points,
"angle":angle
}
return out;
}
//如果是无向图隐藏下面计算与画三角形箭头的代码即可
var obj = {
"startcx":startcx,
"startcy":startcy,
"endcx":endcx,
"endcy":endcy
}
var data = getTriangle(obj);
var angle = [data.angle,endcx,endcy].join(",");
//画三角形箭头
arrowStr += '<polygon points="'+data.points+'" fill:"#171717" transform="rotate('+angle+')"/>';