使用邻接表存储图
广度优先遍历
A->B->C->D->E->F
遍历A元素的时候,将A元素的邻接节点(B、C)插入数组,访问完B,将B的邻接表(C、D)插入数组,C已经在遍历表中了,因此不添加到遍历表中,接下来访问C,将邻接表(D、E)插入遍历数组中,同样,D已经在遍历表中,就不添加进去,避免重复访问。
遍历的过程没有重复元素
访问栈、记录表
//邻接点制成的表
var graph={
"A":["B","C"],
"B":["A","C","D"],
"C":["A","D","E"],
"D":["B","E","F"],
"E":["C","D"],
"F":["D"],
}
//将对象转成Map,方便访问
var graphM=new Map();
for(var [key,value] of Object.entries(graph)){
graphM.set(key,value);
}
//console.log(graphM);
// B—— D
// /| /|\
//A | / | F
// \|/ |
// C——E
function BFS(root,s){
//用来记录访问过的节点 Set()中不能有重复的元素
var remark=new Set();
//遍历节点邻接表存储
var arr=[];
//用来返回的存储广度优先的遍历顺序
var bfs=[];
arr.push(s);
//将当前遍历到的元素的父元素存储起来,方便最短路径进行访问
//A的父元素为空
var parent=new Map([[s,NaN]]);
remark.add(s);
var vet;
while(arr.length>0){
//从队列中将顶点取出,添加邻接表
vet=arr.shift();
bfs.push(vet);
// console.log(vet,root.get(vet));
for(var item of root.get(vet)){
if(!remark.has(item)){
//将节点存储
arr.push(item);
remark.add(item);
parent.set(item,vet);
}
}
}
//返回广度遍历结果
return bfs;
//返回父节点表
//return parent;
}
// arr:用来存储访问的结果
var bfs=BFS(graphM,'A');
console.log(bfs);
深度优先遍历
以A节点为起点,深度遍历,访问A节点,将A的邻接表(B、C)都未访问过,将C、B压入栈中,选择访问B节点,将B的邻接表(C、D)压入栈中,由于C已经访问过,只将D压入栈中,栈顶弹出D,访问D的邻接表(C、E、F、B),其中C、B都访问过了,只将E、F压入栈中,从栈顶依次弹出进行访问。
只要将节点压入栈中,节点的状态就变成已访问。
//邻接点制成的表
var graph={
"A":["B","C"],
"B":["A","C","D"],
"C":["A","D","E"],
"D":["B","E","F"],
"E":["C","D"],
"F":["D"],
}
//将对象转成Map,方便访问
var graphM=new Map();
for(var [key,value] of Object.entries(graph)){
graphM.set(key,value);
}
//console.log(graphM);
// B—— D
// /| /|\
//A | / | F
// \|/ |
// C——E
//深度优先遍历
function DFS(root,s){
//用来记录访问过的节点
var remark=new Set();
//用来存储剩余访问的节点
var arr=[];
//用来记录深度遍历节点
var dfs=[];
arr.push(s);
//标记访问过的节点
remark.add(s);
var vet;
while(arr.length>0){
//从栈顶弹出当前需要访问的节点
vet=arr.pop();
dfs.push(vet);
for(var item of root.get(vet)){
//如果当前的邻接表中节点没有访问过 放入栈中,并添加访问记号
if(!remark.has(item)){
arr.push(item);
remark.add(item);
}
}
}
return dfs;
}
var dfs=DFS(graphM,'A');
console.log(dfs);
最短路径:
BFS遍历,得到的父节点数组,根据直接父节点数组得到
假如获得从A-E的最短路径:
利用终点E和parent父节点数组,不断的回溯,直到找到A节点
打印路径,在访问的时候,将parent
遍历到路径节点从数组的头部开始添加
var parent=BFS(graphM,'A');
console.log(parent);
var v='E';
// console.log(v)
var path=[];
//从E开始向前搜索,直到搜索到的节点值为空
while(v){
//从数组头部添加内容
path.unshift(v);
v=parent.get(v);
}
console.log(path);
狄克拉斯特Dijkstra
计算加权图的最短路径:
- 每次遍历更新当前节点的路径,选择最短的路径弹出数组,访问该节点的邻居节点,选择没有访问过的压入数组;下次访问仍然选择数组中最小的进行访问。直到访问结束。
加权图,使用邻接表初始化。
var map=new Map()
var graph={
"A":new Map([["B",5],["C",1]]),
"B":new Map([["A",5],["C",2],["D",1]]),
"C":new Map([["A",1],["B",2],["D",4],["E",8]]),
"D":new Map([["B",1],["C",4],["E",3],["F",6]]),
"E":new Map([["C",8],["D",3]]),
"F":new Map([["D",6]]),
}
//将邻接表对象转成Map
for(var [key,item] of Object.entries(graph)){
map.set(key,item);
}
- 获取当前unvisited节点中最小权值的节点:
loer_dist_node=null:目的当主体函数中未访问节点表中的节点为空时,返回null便于结束此处查找。
function findMin(dists,visited){
var lower_dist=Infinity;
//当访问到最后一个节点的时候,在查找下一个最小节点的时候,返回null访问结束
var lower_dist_node=null;
//遍历距离列表中的所有节点,获得dist最小且未访问过的节点
for(var [vertx,dist] of dists){
//如果当前节点离起点的距离小于原始存储的节点,并且没有访问过
if(dist<lower_dist&& !visited.has(vertx)){
// 获得当前为遍历的最小距离的节点
lower_dist_node=vertx;
//记录当前最小的距离,便于下次访问
lower_dist=dist;
}
}
//遍历结束,返回当前获得的未访问过,代价最小的节点
return lower_dist_node;
}
- 更新
dists表
、parent表
当找到距离表dists
中的最小节点
,遍历该节点的邻居节点root.get(node)
,邻居节点vertx
的边距离w
,根据当前遍历节点node
获得从起点到vertx
的距离(w+dists.get(node)
)小于
dists表中vertx的距离(dists.get(vertx)
),更新dists表,更新vertx的父节点为node。此次遍历结束
,将当前节点node的状态设置为visited
,并添加dijkstra访问路径表中,继续下次循环。直至node=null
function Dijkstra(root,s){
//用来存储剩余访问的节点
// var arr=[];
// arr.push(s);
//访问当前图中,从起点开始权值最小的节点
//距离表
var dists=new Map();
//遍历所有节点将开销全都初始化为无群大
for(var key of root.keys()){
dists.set(key,Infinity);
}
//将起点的距离设置为0
dists.set(s,0);
//打印距离
// console.log(dists);
//父节点存储表
var parents=new Map([[s,NaN]]);
//存储访问过的节点
var visited=new Set();
//当前访问的节点
// var pair;
//定义获得当前边的权重
// var w;
//当前访问节点的邻接表
// var vertx;
//dijsktra访问路径
var dijkstra=[];
var node=findMin(dists,visited);
console.log(node);
while(node){
//此时将邻接表加入其中
//vertx node节点的邻居节点
//w vertx节点的权值
for(var [vertx,w] of root.get(node)){
// if(!visited.has(vertx)){
if(dists.get(node)+w<dists.get(vertx))
//更新当前节点的距离表
dists.set(vertx,w+dists.get(node));
//更新当前节点的父节点表
parent.set(vertx,node);
// console.log(parent);
// }
}
//将当前父节点标记未处理过
visited.add(node);
//将访问路径存储起来
dijkstra.push(node);
// console.log('路径:'+dijkstra);
node=findMin(dists,visited);
}
console.log(dijkstra,dists);
return dists;
}