一、选择合适路网存储结构的必要性
道路网络的存储方法不但对路径优化算法的性能有重要影响,而且是车辆定位、地图匹配以及路径引导的基础。在图论中诸如Dijkstra、A*等经典的路径优化算法,在实际应用时实时性较差,主要原因有:
- 1)路网是有向图
- 2)道路交叉口与图论中节点不同,它本身有权重且常常不唯一
- 3)路网中存在各种转向限制
因此需要使用合适的路网存储结构提高算法效率。
二、图的分类
有向图、无向图
带权图、不带权图
三、路网内存中的存储机制
下面介绍的是图在内存中存储的数据结构。尝试用以下3种方法存储下图(把它看做一个双向连通的有向图,括号中为节点编号),无向图是特殊的有向图自然也可以用以下方法进行存储。
1.邻接矩阵(顺序存储)
设G=(V,E)
是具有n个节点的图,顶点序号依次为0,1,...,n-1,则G的邻接矩阵是具有如下定义的n阶方阵A:
A[i][j]=0表示顶点i与顶点j邻接,即i与j之间存在边或者弧;
A[i][j]=1表示顶点i与顶点j不邻接
(0<=i,j<=n-1)
1)代码
public class MGraph {
// 图的节点
class Node {
// 节点编号
int num;
// 节点属性
char info;
}
// 节点
Node[] nodes;
// 边
double[][] edges;
// 节点个数
int n;
// 边个数
int e;
}
2)图示
nodes数组
邻接矩阵
3)评价
实际路网是大型稀疏图,不建议采用
2.邻接表(链式存储)
1)代码
public class AGraph {
class ArcNode {
// 该边指向的节点
int num;
// ...其他信息
// 下一指针域
ArcNode next;
}
class Node {
// 节点编号
int num;
// 信息
char info;
// 从节点num出发的边存在后面的链表中
ArcNode nextArc;
}
// 节点
Node[] nodes;
// 节点数
int n;
// 边数
int e;
}
2)图示
3)评价
常用于稀疏图,节省存储空间
3.前向关联边
Dial于1979年提出。道路网络是大型稀疏网络,具有转向限制、节点带权重等特点
前向关联边结构中主要有三个数组,
- 一个用来存储与弧有关的数据
- 另一个用来存储与节点有关的数据
- 将每个节点的所有邻接节点按顺序存储在数组中一块连续的空间中,并记录每一个节点在所指向的节点数组中第一次出现的位置和邻接节点个数总和。
1)代码
public class MyGraph {
// 节点个数
public int n;
// 边个数
public int e;
// 结点集合
public Node[] nodes;
// 所有节点发出的所有边(按照特定的顺序排列起来)
public int[] linkedEdges;
// 边集合
public Edge[] edges;
public MyGraph() {}
public MyGraph(int n, int e) {
this.n = n;
this.e = e;
// 从数组下标0开始存储
nodes = new Node[n];
edges = new Edge[e];
linkedEdges = new int[e];
}
public static class Node implements Comparable < Node > {
// 节点编号
public int nodeId;
// 节点名称
public String nodeName;
// 经度
public float longtitude;
// 纬度
public float latitude;
// TODO: 2018/10/30 节点类型
public int type;
// 是否有红绿灯
public boolean hasLed;
// 从该节点发出的弧个数
public int count;
// 该节点发出的第一个弧在linkEdges中的下标
public int linkEdgesBeginIndex;
// 该节点处的转向限制
public List < ForbiddenTurn > forbiddenTurnList;
// 该节点数据是否为手动采集
public boolean fromManual;
public Node() {}
public Node(int nodeId) {
this.nodeId = nodeId;
}
// 定义默认访问规则
@Override
public int compareTo(Node o) {
return this.nodeId - o.nodeId;
}
}
public static class Edge {
// 路段编号
public int edgeId;
// 路段名称
public String edgeName;
// 道路长度
public double length;
// 道路宽度
public double width;
// 车道数
public int laneNum;
// 道路等级:高速公路、国道、省道、县道、乡道、城市快速路等
// 开始节点编号
public Node enterNode;
// 结束节点编号
public Node exitNode;
// TODO: 2018/10/30 路段属性
// TODO: 2018/10/30 路段权值
// 路段权值
public double weight;
// 该道路是否连通
public boolean isConnected;
// 是否为手动采集
public boolean fromManual;
}
public static class ForbiddenTurn {
// 结点编号
public int nodeId;
// 进节点编号
public int enterNodeId;
// 出节点编号
public int exitNodeId;
public ForbiddenTurn() {}
}
}
2)图示
将nodes数组按照节点编号从小到大排序(为了后面根据节点编号查找节点时可使用二分查找)
为了表达禁行关系,需要把每个节点的禁行关系单独进行存储。为每个节点添加一个标志信息,该标志信息提示该节点处有没有转向限制,同时将转向禁止的进入节点和出去节点存储起来(下图中需存储节点A的禁行关系,进入节点是B,出去节点是C),当路径规划扩展到节点A时,如果下一步扩展的节点是C,则需要检查进入的节点是否是节点B,如果是的话扩展失败(因为此交叉路口时此方向转向禁止)
3)评价
路网作为大型稀疏网络,该存储方式可节省存储空间,并表达交叉口转向限制。