前言
我们上一篇描述了需求,这一篇详细地实现图类。
数据成员
public class SparseGraph:GraphRemote
{
protected Dictionary<int, object> _objects = new Dictionary<int, object>();
protected List<VertexBase> _verts = new List<VertexBase>();
}
- 首先有两个protected成员,提供给子类使用。
- _objects是存放实体的表,如果没有实体可以忽略。
- _verts是用来存放顶点数组的,邻接表的数组部分
从xml加载
public virtual bool LoadFromFile(string xmlname)
{
XmlDocument doc = new XmlDocument();
string path = @"Assets/Resources/"+xmlname;
try
{
doc.Load(path);
}
catch (Exception e)
{
MonoBehaviour.print(e);
return false;
}
var root = doc.SelectSingleNode("ROOT");
var vertList = root.ChildNodes;
foreach (XmlNode vert in vertList)
{
//entity
TestNode tn = new TestNode();
VertexBase vertex = new VertexBase(this._objects);
//id
XmlNode idEle = vert.FirstChild;
string idStr = idEle.InnerText;
int idInt = 0;
int.TryParse(idStr, out idInt);
vertex.Index = idInt;
this._objects.Add(idInt, tn);
_verts.Add(vertex);
//edges
XmlNode edges = idEle.NextSibling;
var edgeList = edges.ChildNodes;
foreach (XmlNode edgeData in edgeList)
{
//to
var toData = edgeData.FirstChild;
string s_edge_to = toData.InnerText;
int i_edge_to = 0;
int.TryParse(s_edge_to, out i_edge_to);
//cost
var costData = toData.NextSibling;
string s_edge_cost = costData.InnerText;
float f_edge_cost = 0.0f;
float.TryParse(s_edge_cost, out f_edge_cost);
//edge
EdgeBase edge = new EdgeBase();
edge.From = idInt;
edge.To = i_edge_to;
edge.Cost = f_edge_cost;
vertex.AddEdge(edge);
}
//type
XmlNode nodeType = edges.NextSibling;
string s_nt = nodeType.InnerText;
tn.type = CommonUtils.SimpleFunctions.stringToType(s_nt);
//pos
XmlNode posData = nodeType.NextSibling;
string s_pos = posData.InnerText;
var posArr = s_pos.Split(',');
float x = 0f;
float y = 0f;
float z = 0f;
float.TryParse(posArr[0], out x);
float.TryParse(posArr[1], out y);
float.TryParse(posArr[2], out z);
tn.pos = new Vector3(x, y, z);
//extra info
}
calculateCostByDis();
_verts.Sort();
//re sort edges
foreach (var v in _verts)
{
v.Edges.Sort();
}
return true;
}
- 从xml中实例图数据,可以自己定制,virtual以供重写。
这里我们提供一个xml表的样式:
<?xml version="1.0" encoding="utf-8"?>
<ROOT>
<Vertex>
<Idx>1</Idx>
<Edges>
<Edge>
<To>2</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>3</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>0,0,0</Pos>
</Vertex>
<Vertex>
<Idx>2</Idx>
<Edges>
<Edge>
<To>3</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>5</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>10,0,0</Pos>
</Vertex>
<Vertex>
<Idx>3</Idx>
<Edges>
<Edge>
<To>1</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>2</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>4</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>6</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>7</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>10,0,10</Pos>
</Vertex>
<Vertex>
<Idx>4</Idx>
<Edges>
<Edge>
<To>3</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>5</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>21,0,4</Pos>
</Vertex>
<Vertex>
<Idx>5</Idx>
<Edges>
<Edge>
<To>2</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>4</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>8</To>
<Cost>1</Cost>
</Edge>
<Edge>
<To>9</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>11,0,-13</Pos>
</Vertex>
<Vertex>
<Idx>6</Idx>
<Edges>
<Edge>
<To>3</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>1,0,19</Pos>
</Vertex>
<Vertex>
<Idx>7</Idx>
<Edges>
<Edge>
<To>3</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>20,0,20</Pos>
</Vertex>
<Vertex>
<Idx>8</Idx>
<Edges>
<Edge>
<To>5</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>4,0,-22</Pos>
</Vertex>
<Vertex>
<Idx>9</Idx>
<Edges>
<Edge>
<To>5</To>
<Cost>1</Cost>
</Edge>
</Edges>
<NodeType>normal</NodeType>
<Pos>29,0,-13</Pos>
</Vertex>
</ROOT>
这个文件渲染出来的图(unity中)如下:
此处的cost都是1,因为我在程序里根据距离重新计算了
void calculateCostByDis()
{
for (int i = 0; i < _verts.Count; ++i)
{
var eachV = _verts[i];
for (int j = 0; j < eachV.Edges.Count; ++j)
{
var eachE = eachV.Edges[j];
float dis = Vector3.Distance((_objects[eachE.From] as TestNode).pos,
(_objects[eachE.To] as TestNode).pos);
eachE.Cost = dis;
}
}
}
保存
和读取同理,此处省略。
添加和删除顶点
public bool AddVertex(TestNode refTn, VertexBase vert, int idx)
{
if ((refTn == null) || (vert == null))
{
return false;
}
if (_objects.ContainsKey(idx))
return false;
//注意此处将索引和实体同步,然后将顶点排序
_objects.Add(idx, refTn);
vert.Index = idx;
_verts.Add(vert);
_verts.Sort();
return true;
}
public bool RemoveVertex(int idx)
{
if (!_objects.ContainsKey(idx))
{
return false;
}
_objects.Remove(idx);
object vertRef = null;
foreach (VertexBase v in _verts)
{
if (v.Index == idx)
{
vertRef = v;
}
}
if (vertRef == null)
return false;
_verts.Remove(vertRef as VertexBase);
return true;
}
添加和删除边
注意数据验证,有些是不合理的,不允许添加或删除
public bool AddEdge(EdgeBase e)
{
if (_objects.ContainsKey(e.From))
{
foreach (VertexBase v in _verts)
{
if (v.Index == e.From)
{
v.AddEdge(e);
}
}
}
return false;
}
public bool RemoveEdge(int from, int to)
{
if (!_objects.ContainsKey(from))
return false;
foreach (VertexBase v in _verts)
{
if (v.Index == from)
{
foreach (EdgeBase e in v.Edges)
{
if (e.To == to)
{
v.Edges.Remove(e);
return true;
}
}
}
}
return false;
}
清空
使用索引的好处来了,清空极其方便。
public void Clear()
{
_objects.Clear();
_verts.Clear();
}
根据id获取顶点
虽然我们根据index对顶点进行了排序,但是一旦index不是连续的(比如过程中删除了一个顶点),我们就不能通过_verts[index-1]来访问(顶点从1开始,不是零)
public VertexBase GetVertByIdx(int index)
{
if (_verts[index - 1].Index == index)
{
return _verts[index - 1];
}
else
{
foreach (var v in _verts)
{
if (v.Index == index)
{
return v;
}
}
}
return null;
}