权重非负图的最短路径
算法性质
求取非负权重图所有源点可达节点从源点可达的一条最短路径。
因为对输入多了要求,所以,相比无要求的处理算法,
可以有更优的效率。
接口设计
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*> RunForWeightNoNegtive(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>::RunForWeightNoNegtive(const Key& nSourceKey_)
{
InnerGraph::Node* _pGraphNode = m_nGraph.SearchNode(nSourceKey_);
if (_pGraphNode == nullptr)
{
throw "can not find key in graph";
}
DataStruct::Array::DynArray<Node*> _arrpNodes;
DataStruct::Array::DynArray<Node*> _arrpRetNodes;
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();
}
DataStruct::Array::DynArray<typename InnerGraph::Node*> _arrGraphNodes = m_nGraph.GetNodesArray();
DataStruct::Array::DynArray<typename InnerGraph::Edge*> _arrGraphEdges = m_nGraph.GetEdgesArray();
while (_arrpNodes.GetSize() > 0)
{
Node* _pNode = nullptr;
// 从节点集合1选出距离最短的Node
for (int _i = 0; _i < _arrpNodes.GetSize(); _i++)
{
if (_arrpNodes[_i]->m_pPreNode != nullptr
|| _arrpNodes[_i]->m_pGraphNode == _pGraphNode)
{
if (_pNode == nullptr
|| _pNode->m_nDistance > _arrpNodes[_i]->m_nDistance)
{
_pNode = _arrpNodes[_i];
}
}
}
if (_pNode != nullptr)
{
// 选出的Node放入另一Node集合
_arrpNodes.DeleteByValue(_pNode);
_arrpRetNodes.Add(_pNode);
// 对所有该节点可达节点
DataStruct::Array::DynArray<Key> _arrDestinations = _pNode->m_pGraphNode->GetDests();
for (int _i = 0; _i < _arrDestinations.GetSize(); _i++)
{
Node* _pDestNode = nullptr;
m_nNodeMappingTree.Search(_arrDestinations[_i], _pDestNode);
if (_pDestNode == nullptr)
{
throw "cannot find dest in graph";
}
InnerGraph::EdgeIdentity _nIdentity;
_nIdentity.m_nStartKey = _pNode->m_pGraphNode->GetPair().m_nKey;
_nIdentity.m_nEndKey = _arrDestinations[_i];
InnerGraph::Edge* _pEdge = m_nGraph.SearchEdge(_nIdentity);
if (_pEdge == nullptr)
{
throw "cannot find edge";
}
// 对可达节点进行松弛判断
if ((_pDestNode->m_pPreNode == nullptr && _pDestNode->m_pGraphNode != _pGraphNode)
|| _pDestNode->m_nDistance > _pNode->m_nDistance + _pEdge->m_nWeight)
{
_pDestNode->m_pPreNode = _pNode;
_pDestNode->m_nDistance = _pNode->m_nDistance + _pEdge->m_nWeight;
}
}
}
// 选不出来,表示剩余Node均为不可达的
else
{
for (int _i = 0; _i < _arrpNodes.GetSize(); _i++)
{
_arrpRetNodes.Add(_arrpNodes[_i]);
}
_arrpNodes.DeleteAll();
}
}
return _arrpRetNodes;
}
算法目标&正确性证明
算法对输入要求:权重图所有边的权重 大于等于0
算法执行过程
初始化,得到初始节点集合1
循环迭代
从节点集合1选出距离最小的节点
1. 若不存在这样的节点,表示,节点集合1中所有节点都是通过s,不可达的。
2. 若存在这样的节点p,
对p所有可达节点,执行松弛操作
将节点p放入节点集合2
对于任意通过s可达的节点p
必然存在某条s~p的最短路径s->x1->...->xn->p
易于证明,
s->x1
s->x1->x2
....
s->x1->...->xn
分别是s到x1,x2,...,xn的一条最短路径
循环不变式:
每次迭代中,
1. 我们若能选出节点,
则,选出节点,必是最短路径信息已知的节点
2. 我们若无法选出节点,
则,表示所有可达节点我们都已考虑,剩下的全部是不可达的节点。
证明
第一次,选择时,我们必然选择s,
此时,选出的s的最短路径信息已知。
对第k次,
依据循环不变式,第1,...k-1次迭代后,循环不变式均满足
如果我们选出p
则,
存在s到p的一条关系链
则s->x1->...->xn->p
由于p.m_nDistance是所有剩余节点中距离最小的
故此时必有x1,...,xn均在之前已被选择节点集合
s->x1
s->x1->x2
....
s->x1->...->xn
分别是s到x1,x2,...,xn的一条最短路径
采用反证法
假设
s->x1->...->xn->p不是s~p的一条最短路径
因为此时,首先p必然是从s可达的
设s->y1->...->ym->p为s~p的一条最短路径
则,
s->y1
s->y1->y2
....
s->y1->...->ym
分别是s到y1,y2,...,ym的一条最短路径
因为(s->y1).m_nDistance < ... < (s->y1->...->ym).m_nDistance < (s->y1->...->ym->p).m_nDistance
故,此时y1,...ym必然已经被选入节点集合2
这种情况下,当初处理ym时,
必然已经确定了s->y1->...->ym->p的最短路径信息
由于最短路径信息一旦确定后续,松弛过程不会再变化【依赖于所有边的权重均大于等于0的前提】
所以此时和s->x1->...->xn->p的情况不符合
【其中y1->...->ym != x1->...->xn】
故,s->x1->...->xn->p是s~p的一条最短路径
第k次迭代时,假设我们选不出p
则,必有对于剩余所有节点q,有q->m_pPreNode == null,
我们证明,此时所有剩余节点均是从s不可达的
假设,存在某节点k从s可达
则必有s~k的某最短路径
s->x1->...->xn->k
s->x1
s->x1->x2
....
s->x1->...->xn
分别是s到x1,x2,...,xn的一条最短路径
因为,本次无法选出p,
所以,本次选择时,x1,...xn必然已经被选择到节点集合2
故,在xn选入时,必然会设置k的m_nDistance,
使其相比其它不可达节点,变成一个可达的距离。
这种情况下,本次必然会选出k
这和实际情况不符,
故假设不成立。
综合,本次迭代后,循环不变式依然满足。
因为,
每次迭代我们总是可以选出一个最短路径已知节点或知道所有剩余节点均为不可达节点信息
所以,最多执行了k1次迭代后,
对于所有可达节点,其最短路径信息,均已经被确定。