无环图的单源最短路径
算法性质
算法对关联的图的要求:无环的权重图。
算法性质:
可求得图G所有s可达节点p,s~p的一条最短路径。
接口设计
template<typename Key, typename Value>
class ShorestPath
{
public:
class Node;
typename typedef DataStruct::GraphStruct::Graph<Key, Value> InnerGraph;
typename typedef DataStruct::Tree::SortedBalanceBinaryTree<Key, Node*> InnerTree;
class Node
{
public:
double GetDistance()
{
return m_nDistance;
}
typename InnerGraph::Node* GetGraphNode()
{
return m_pGraphNode;
}
Node* GetPreNode()
{
return m_pPreNode;
}
private:
Node()
{
m_nDistance = -1.0;
m_pGraphNode = nullptr;
m_pPreNode = nullptr;
}
Node(typename InnerGraph::Node* pGraphNode_)
{
m_nDistance = -1.0;
m_pGraphNode = pGraphNode_;
m_pPreNode = nullptr;
}
~Node()
{
}
void Reset()
{
m_pPreNode = nullptr;
m_nDistance = 0;
}
private:
double m_nDistance;
typename InnerGraph::Node* m_pGraphNode;
Node* m_pPreNode;
friend class ShorestPath;
};
ShorestPath(const InnerGraph& nGraph_);
~ShorestPath();
DataStruct::Array::DynArray<Node*> RunForNoCircle(const Key& nSourceKey_);
private:
ShorestPath(const ShorestPath&) = default;
ShorestPath& operator=(const ShorestPath&) = default;
private:
const InnerGraph& m_nGraph;
InnerTree m_nNodeMappingTree;
};
实现
构造
template<typename Key, typename Value>
ShorestPath<Key, Value>::ShorestPath(const InnerGraph& nGraph_)
: m_nGraph(nGraph_)
{
DataStruct::Array::DynArray<typename InnerGraph::Node*> _arrGraphNodes = m_nGraph.GetNodesArray();
for (int _i = 0; _i < _arrGraphNodes.GetSize(); _i++)
{
Node* _pNode = nullptr;
try
{
_pNode = new Node(_arrGraphNodes[_i]);
}
catch (...)
{
_pNode = nullptr;
throw "out of memory";
}
InnerTree::Pair _nPair;
_nPair.m_nKey = _arrGraphNodes[_i]->GetPair().m_nKey;
_nPair.m_nValue = _pNode;
m_nNodeMappingTree.Add(_nPair);
}
}
析构
template<typename Key, typename Value>
ShorestPath<Key, Value>::~ShorestPath()
{
DataStruct::Array::DynArray<InnerTree::Pair> _arrTreePairs = m_nNodeMappingTree.GetArray();
for (int _i = 0; _i < _arrTreePairs.GetSize(); _i++)
{
delete (_arrTreePairs[_i].m_nValue);
_arrTreePairs[_i].m_nValue = nullptr;
}
}
算法运行
template<typename Key, typename Value>
DataStruct::Array::DynArray<typename ShorestPath<Key, Value>::Node*> ShorestPath<Key, Value>::RunForNoCircle(const Key& nSourceKey_)
{
InnerGraph::Node* _pSourceNode = m_nGraph.SearchNode(nSourceKey_);
if (_pSourceNode == nullptr)
{
throw "can not find key in graph";
}
DataStruct::Array::DynArray<Node*> _arrpNodes;
DataStruct::Array::DynArray<InnerTree::Pair>_arrTreePairs = m_nNodeMappingTree.GetArray();
for (int _i = 0; _i < _arrTreePairs.GetSize(); _i++)
{
_arrpNodes.Add(_arrTreePairs[_i].m_nValue);
_arrpNodes[_i]->Reset();
}
TopologySort<Key, Value> _alTopologySort(m_nGraph);
DataStruct::Array::DynArray<typename DepthFirstVisit<Key, Value>::Node*> _arrTempNodes = _alTopologySort.Run();
DataStruct::Array::DynArray<Key> _arrKeys;
for (int _i = 0; _i < _arrTempNodes.GetSize(); _i++)
{
InnerGraph::Node* _pNode = _arrTempNodes[_i]->GetGraphNode();
if (_pNode == nullptr)
{
throw "graph node not exist";
}
_arrKeys.Add(_pNode->GetPair().m_nKey);
}
// 对按拓扑排序处理的所有节点
for (int _i = 0; _i < _arrKeys.GetSize(); _i++)
{
int _nKey = _arrKeys[_i];
Node* _pNode = nullptr;
m_nNodeMappingTree.Search(_nKey, _pNode);
if (_pNode == nullptr)
{
throw "node not exist";
}
InnerGraph::Node* _pGraphNode = _pNode->m_pGraphNode;
DataStruct::Array::DynArray<Key> _arrDestKeys = _pGraphNode->GetDests();
// 依次处理每个节点所有可达节点
for (int _j = 0; _j < _arrDestKeys.GetSize(); _j++)
{
if (_pNode->m_pPreNode != nullptr
|| _pNode->m_pGraphNode == _pSourceNode)
{
Node* _pDestNode = nullptr;
m_nNodeMappingTree.Search(_arrDestKeys[_j], _pDestNode);
if (_pDestNode == nullptr)
{
throw "dest node not exist";
}
InnerGraph::EdgeIdentity _nIdentity;
_nIdentity.m_nStartKey = _pNode->m_pGraphNode->GetPair().m_nKey;
_nIdentity.m_nEndKey = _pDestNode->m_pGraphNode->GetPair().m_nKey;
InnerGraph::Edge* _pEdge = nullptr;
_pEdge = m_nGraph.SearchEdge(_nIdentity);
if (_pEdge == nullptr)
{
throw "edge not exist";
}
// 对可达节点进行松弛操作
if ((_pDestNode->m_pPreNode == nullptr && _pDestNode->m_pGraphNode != _pSourceNode)
|| (_pDestNode->m_nDistance > _pNode->m_nDistance + _pEdge->m_nWeight))
{
_pDestNode->m_pPreNode = _pNode;
_pDestNode->m_nDistance = _pNode->m_nDistance + _pEdge->m_nWeight;
}
}
}
}
return _arrpNodes;
}
算法目标&正确性分析
循环不变式
迭代处理到节点p时,p的s~p的最短路径信息是已知的
初始时,
处理首个节点p0,
1. 若p0不为s,
则,任何其他节点q,不存在q->p0
证明不存在s~p0,采用反证法
假设存在某s~p0,具体为s->x1->...->xk->p0
因为对于任意节点x,不存在x->p0,故假设不成立。
由于不存在s~p0,故p0的最短路径信息是已知的,即默认下的不存在。
2. 若p0为s,则,采用直接证明法
易于知道p0的最短路径信息是已知的。
综合,初始时,循环不变式成立。
迭代处理到节点pk时,证明此时s~pk的最短路径信息是已知的。
依据循环不变式,p0,...,p(k-1)的最短路径信息都是已知的。
1. 假设pk是s可达的。
则对于s~pk的某条最短路径,具体为s->x1->...->xt->pk
证明对于s,x1,...xt中节点必然按s,x1,...xt顺序出现在pk前的主循环的迭代中
首先对于xt,则依据拓扑排序性质,可知xt必然在pk前面
接着对于x(t-1),则依据拓扑排序性质,可知x(t-1)必然在xt前面
...
最后对于s,则依据拓扑排序性质,可知s必然在x1前面
依据最短路径性质
1. 假设存在某最短路径s->x1->....->xn->p
则
s->x1是s~x1最短路径
s->x1->x2是s~x2最短路径
...
s->x1->....->xn是s~xn最短路径
2. 在算法处理中,节点最短路径一旦确定,后续算法运行中将不会再发生变化
由于依据循环不变式,xt的最短路径信息是已经知道的
则,在对xt做所有可达节点松弛操作时,必然会设置pk的最短路径信息
故,pk的最短路径信息是已经存在的。
2. 假设pk是s不可达的
由于不可达,故pk维持默认值
也即s~pk的最短路径信息是已知的
综合,循环不变式成立
算法正确性证明
依据迭代过程的循环不变式,
易于知道,迭代处理后,将知道所有节点p的s~p的最短路径信息