UNPKG

8.48 kBPlain TextView Raw
1import { Dijkstra } from './dijkstra.utils';
2export class GraphUtils {
3 /**
4 * 把节点集和边集转换成图矩阵
5 * @param nodes 节点集
6 * @param edges 边集
7 * @param directed 是否有向,缺省是无向图
8 * @param distanceProp 边上的距离属性名,缺省为distance
9 * @return 图矩阵
10 */
11 static toGraphMatrix(nodes: NodeBase<any>[], edges: EdgeBase<any>[], directed = false, distanceProp = "distance"): number[][] {
12 let m = GraphUtils.getMatrix(nodes.length);
13 let nodeMap = new Object();
14 let idx = 0;
15 for (let vertex of nodes) {
16 nodeMap[vertex.id] = idx;
17 idx++;
18 }
19 for (let edge of edges) {
20 let distance = edge[distanceProp];
21 if (!distance) {
22 distance = 1;
23 }
24 m[nodeMap[edge.source.id]][nodeMap[edge.target.id]] = distance;
25 if (!directed) {
26 m[nodeMap[edge.target.id]][nodeMap[edge.source.id]] = distance;
27 }
28 }
29 return m;
30 }
31 private static getMatrix(length: number): number[][] {
32 let m = new Array<number[]>(length);
33 for (let idx = 0; idx < m.length; idx++) {
34 m[idx] = new Array<number>(length);
35 }
36 return m;
37 }
38 /** 从某一源点出发,找到到所有结点的最短路径
39 *
40 * @param nodes 节点集合
41 * @param edges 边集合
42 * @param start 起点
43 * @param directed 是否有向图,缺省为无向
44 */
45 static getShortedPathAll(nodes: NodeBase<any>[], edges: EdgeBase<any>[], start: NodeBase<any>, directed = false): NodeBase<any>[][] {
46 let graph = GraphUtils.toGraphMatrix(nodes, edges, directed);
47 let pos: number = GraphUtils.getNodePosition(nodes, start);
48 let paths = GraphUtils.getMatrix(nodes.length);
49 let dists = Dijkstra.getShortedPathAll(graph, pos, paths);
50 let pathNodeMatrix = new Array();
51 for (let path of paths) {
52 let pathNodes = new Array<NodeBase<any>>();
53 for (let n of path) {
54 if (n == undefined) {
55 break;
56 }
57 pathNodes.push(nodes[n]);
58 }
59 pathNodeMatrix.push(pathNodes);
60 }
61 return pathNodeMatrix;
62 }
63 /** 从某一源点出发,找到到某一结点的最短路径
64 *
65 * @param nodes 节点集合
66 * @param edges 边集合
67 * @param start 起点
68 * @param end 终点
69 * @param directed 是否有向图,缺省为无向
70 */
71 static getShortedPath(nodes: NodeBase<any>[], edges: EdgeBase<any>[], start: NodeBase<any>, end: NodeBase<any>, directed = false): NodeBase<any>[] {
72 let graph = GraphUtils.toGraphMatrix(nodes, edges, directed);
73 let startPos: number = GraphUtils.getNodePosition(nodes, start);
74 let endPos: number = GraphUtils.getNodePosition(nodes, end);
75 let path = new Array<number>(nodes.length);
76 let dist = Dijkstra.getShortedPath(graph, startPos, endPos, path);
77 let pathNodes = new Array<NodeBase<any>>();
78 for (let n of path) {
79 if (!n) {
80 break;
81 }
82 pathNodes.push(nodes[n]);
83 }
84 return pathNodes;
85 }
86
87 /**
88 * 找到与指定节点相连的节点
89 * @param nodes 节点集
90 * @param edges 边集
91 * @param start 指定节点
92 * @param directed 是否有向图,缺省是无向图
93 */
94 static getConnectedNodes(nodes: NodeBase<any>[], edges: EdgeBase<any>[], start: NodeBase<any>, directed = false): NodeBase<any>[] {
95 let graph = GraphUtils.toGraphMatrix(nodes, edges, directed);
96 let pos: number = GraphUtils.getNodePosition(nodes, start);
97 let m = GraphUtils.getMatrix(nodes.length);
98 let nodeIdxs = GraphUtils.getConectedbyBfs(graph, pos);
99 let cverts = [];
100 for (let i of nodeIdxs) {
101 cverts.push(nodes[i]);
102 }
103 graph = null;
104 m = null;
105 nodeIdxs = null;
106 return cverts;
107 }
108 /**
109 * 找到指定节点在节点集合中的位置
110 * @param nodes 节点集合
111 * @param v0 指定节点
112 * @return 位置,0-based. 如果没找到就返回 undefined;
113 */
114 static getNodePosition(nodes: NodeBase<any>[], v0: NodeBase<any>): number {
115 for (let i = 0; i < nodes.length; i++) {
116 if (nodes[i].id == v0.id) {
117 return i;
118 }
119 }
120 return undefined;
121 }
122 /**
123 * 判断从一个节点出发,到另一个节点是否连通
124 * @param nodes 所有点集
125 * @param edges 所有边集
126 * @param start 起点
127 * @param end 终点
128 * @param directed 是否有向图
129 * @return 是否连通
130 */
131 public static isConnected(nodes: NodeBase<any>[], edges: EdgeBase<any>[], start: NodeBase<any>, end: NodeBase<any>, directed: boolean): boolean {
132 let graph = GraphUtils.toGraphMatrix(nodes, edges, directed);
133 let startPos: number = GraphUtils.getNodePosition(nodes, start);
134 let endPos = GraphUtils.getNodePosition(nodes, end);
135 return GraphUtils.isConectedbyBfs(graph, startPos, endPos);
136
137 }
138 static getConectedbyBfs(graph: number[][], start: number): number[] {
139 let row = graph.length
140 let queue = []
141 let i = start
142 let visited = {}//记录遍历顺序
143 let order = [] //记录顺序,给自己看的
144 queue.push(i) //先把根节点加入
145 while (queue.length) { //如果队列没有被清空,也就是还没遍历完毕
146 for (let j = 0; j < row; j++) {
147 if (graph[i][j]) { //如果是1
148 if (!visited[j]) {
149 queue.push(j)//队列加入未访问
150 }
151 }
152 }
153 queue.shift()//取出队列第一个
154 visited[i] = true//记录已经访问
155 while (visited[queue[0]]) {
156 queue.shift()
157 }
158 order.push(i)//记录顺序
159 i = queue[0]
160 }
161 queue = null;
162 visited = null;
163 return order;
164 }
165 static isConectedbyBfs(arr: number[][], start: number, end: number): boolean {
166 let row = arr.length;
167 let queue = [];
168 let i = start;
169 let visited = {};//记录遍历顺序
170 let order = []; //记录顺序,给自己看的
171 queue.push(i); //先把根节点加入
172 while (queue.length) { //如果队列没有被清空,也就是还没遍历完毕
173 for (let j = 0; j < row; j++) {
174 if (arr[i][j]) { //如果是1
175 if (!visited[j]) {
176 queue.push(j);//队列加入未访问
177 }
178 }
179 }
180 queue.shift();//取出队列第一个
181 visited[i] = true;//记录已经访问
182 console.log("visit node:" + i);
183 if (i == end) {
184 return true;
185 }
186 while (visited[queue[0]]) {
187 queue.shift();
188 }
189 order.push(i);//记录顺序
190 i = queue[0];
191 }
192 queue = null;
193 visited = null;
194 order = null;
195 return false;
196 }
197
198 /**
199 * 把业务点和关联数据转换为图的点和关联数据
200 * @param nodes 业务节点集合,如客户集合
201 * @param edges 业务关联集合,如客户关系集合
202 * @param idProp 可选参数,业务节点ID的属性名,如客户代码“code”,缺省为"id"
203 * @param sourceIdProp 可选参数,业务关联的源ID属性名,如如客户关联关系中的“sourceCustCode”,缺省为"sourceId"
204 * @param targetIdProp 可选参数,业务关联的目标ID属性名,如如客户关联关系中的“targetCustCode”,缺省为"targetId"
205 * @return 位置,0-based. 如果没找到就返回 undefined;
206 */
207 static toGraphData<N, E>(nodes: Array<N>, edges: Array<E>, idProp = "id", sourceIdProp = "sourceId", targetIdProp = "targetId"): GraphData<N, E> {
208 let nodeMap = {};
209 let graphData = new GraphData<N, E>();
210 graphData.nodes = new Array<NodeBase<N>>(nodes.length);
211 graphData.edges = new Array<EdgeBase<E>>(edges.length);
212
213 for (let i = 0; i < nodes.length; i++) {
214 let nd = nodes[i];
215 let node = new NodeBase<any>();
216 node.id = nd[idProp];
217 node.data = nd;
218 nodeMap[node.id] = node;
219 graphData.nodes[i] = node;
220 }
221 for (let i = 0; i < edges.length; i++) {
222 let ed = edges[i];
223 let edge: EdgeBase<E> = new EdgeBase<E>();
224 edge.source = nodeMap[ed[sourceIdProp]];
225 edge.target = nodeMap[ed[targetIdProp]];
226 edge.data = ed;
227 graphData.edges[i] = edge;
228 }
229 nodeMap = null;
230 return graphData;
231 }
232}
233/**
234 * 图相关数据模型,节点基类。
235 */
236export class NodeBase<N> {
237 /**
238 * 节点的ID
239 */
240 id: string;
241 /**
242 * 节点的业务数据,如Customer
243 */
244 data: N;
245}
246/**
247 * 图相关数据模型,连接节点的边基类
248 */
249export class EdgeBase<E> {
250 /**
251 * 源节点的ID
252 */
253 source: NodeBase<any>;
254 /**
255 * 目标节点的ID
256 */
257 target: NodeBase<any>;
258 /**
259 * 源节点到目标节点的距离,用于计算最短路径
260 */
261 distance = 1;
262 /**
263 * 边相关的业务数据,如CustomerRelation
264 */
265 data: E;
266}
267export class GraphData<N, E> {
268 nodes: NodeBase<N>[];
269 edges: EdgeBase<E>[];
270}
\No newline at end of file