權重非負圖的最短路徑
算法性質
求取非負權重圖所有源點可達節點從源點可達的一條最短路徑。
因爲對輸入多了要求,所以,相比無要求的處理算法,
可以有更優的效率。
接口設計
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次迭代後,
對於所有可達節點,其最短路徑信息,均已經被確定。