图的深度优先搜索
算法性质
1.通过主循环,对首个节点p执行Visit
此时所有节点状态均为unvisit
对p执行完visit后
a.将完成所有对p可达节点的访问
b.对于访问中,存在p->x1->...->xn->q访问链的p,q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
c.对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对p,q有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
2.通过主循环,对非首个节点t执行visit
此时图中节点只有状态unvisit和visited
对t执行完visit后
a.将完成在访问前状态为unvisit节点集合中,所有对t可达节点的访问
b.对于访问中,存在p->x1->...->xn->q访问链的p,q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
c.对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对p,q有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
3.假设,p,q为在不同主循环节点visit执行过程被访问的两个节点
若p所在的主循环节点,在q所在的主循环节点之前执行
则有
p->m_nFirstVisitTime < p->m_nLastVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime
接口设计
template<typename Key, typename Value>
class DepthFirstVisit
{
public:
enum State
{
UnVisit,
Visiting,
Visited
};
class Node;
typename typedef DataStruct::GraphStruct::Graph<Key, Value> InnerGraph;
typename typedef DataStruct::Tree::SortedBalanceBinaryTree<Key, Node*> InnerTree;
class Node
{
public:
void Reset()
{
m_pPreNode = nullptr;
m_nFirstVisitTime = 0;
m_nLastVisitTime = 0;
m_nState = State::UnVisit;
}
int GetFirstVisitTime()
{
return m_nFirstVisitTime;
}
int GetLastVisitTime()
{
return m_nLastVisitTime;
}
typename InnerGraph::Node* GetGraphNode()
{
return m_pGraphNode;
}
Node* GetPreNode()
{
return m_pPreNode;
}
private:
Node()
{
m_pGraphNode = nullptr;
m_pPreNode = nullptr;
m_nFirstVisitTime = 0;
m_nLastVisitTime = 0;
m_nState = State::UnVisit;
}
Node(typename InnerGraph::Node* pGraphNode_)
{
m_pGraphNode = pGraphNode_;
m_pPreNode = nullptr;
m_nFirstVisitTime = 0;
m_nLastVisitTime = 0;
m_nState = State::UnVisit;
}
~Node()
{
}
private:
typename InnerGraph::Node* m_pGraphNode;
Node* m_pPreNode;
int m_nFirstVisitTime;
int m_nLastVisitTime;
State m_nState;
friend class DepthFirstVisit;
};
DepthFirstVisit(const InnerGraph& nGraph_);
~DepthFirstVisit();
DataStruct::Array::DynArray<Node*> Run();
private:
void Visit(Node* pNode_, int& nTime_);
private:
DepthFirstVisit(const DepthFirstVisit& nDFV_) = default;
DepthFirstVisit& operator=(const DepthFirstVisit& nDFV_) = default;
private:
InnerGraph m_nGraph;
InnerTree m_nNodeMappingTree;
};
实现
构造
template<typename Key, typename Value>
DepthFirstVisit<Key, Value>::DepthFirstVisit(const InnerGraph& nGraph_)
: m_nGraph(nGraph_)
{
DataStruct::Array::DynArray<InnerGraph::Node*> _arrGraphNodes = m_nGraph.GetNodesArray();
for (int _i = 0; _i < _arrGraphNodes.GetSize(); _i++)
{
if (_arrGraphNodes[_i] == nullptr)
{
throw "graph node cannot be null";
}
Node* _pNode = nullptr;
try
{
_pNode = new Node(_arrGraphNodes[_i]);
}
catch (...)
{
_pNode = nullptr;
throw "out of memory";
}
InnerTree::Pair _nPair;
InnerGraph::Pair _nGraphPair = _arrGraphNodes[_i]->GetPair();
_nPair.m_nKey = _nGraphPair.m_nKey;
_nPair.m_nValue = _pNode;
m_nNodeMappingTree.Add(_nPair);
}
}
析构
template<typename Key, typename Value>
DepthFirstVisit<Key, Value>::~DepthFirstVisit()
{
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 DepthFirstVisit<Key, Value>::Node*> DepthFirstVisit<Key, Value>::Run()
{
int _nTime = 0;
DataStruct::Array::DynArray<InnerTree::Pair> _arrTreePairs = m_nNodeMappingTree.GetArray();
// 对于图中所有节点执行迭代处理
for (int _i = 0; _i < _arrTreePairs.GetSize(); _i++)
{
Node* _pNode = _arrTreePairs[_i].m_nValue;
if (_pNode->m_nState == State::UnVisit)
{
Visit(_pNode, _nTime);
}
}
DataStruct::Array::DynArray<Node*> _arrpNodes;
_arrTreePairs = m_nNodeMappingTree.GetArray();
for (int _i = 0; _i < _arrTreePairs.GetSize(); _i++)
{
_arrpNodes.Add(_arrTreePairs[_i].m_nValue);
}
return _arrpNodes;
}
template<typename Key, typename Value>
void DepthFirstVisit<Key, Value>::Visit(Node* pNode_, int& nTime_)
{
nTime_++;
pNode_->m_nFirstVisitTime = nTime_;
pNode_->m_nState = State::Visiting;
DataStruct::Array::DynArray<Key> _arrDestinations = pNode_->m_pGraphNode->GetDests();
for (int _k = 0; _k < _arrDestinations.GetSize(); _k++)
{
Node* _pDestNode = nullptr;
bool _bRet = m_nNodeMappingTree.Search(_arrDestinations[_k], _pDestNode);
if (_bRet == false)
{
throw "node key cannot find in graph's nodes";
}
if (_pDestNode->m_nState == State::UnVisit)
{
_pDestNode->m_pPreNode = pNode_;
Visit(_pDestNode, nTime_);
}
}
nTime_++;
pNode_->m_nLastVisitTime = nTime_;
pNode_->m_nState = State::Visited;
}
算法目标&算法的性质证明
1.通过主循环,对首个节点p执行Visit
此时所有节点状态均为unvisit
对p执行完visit后
a.将完成所有对p可达节点的访问【采用数学归纳法证明--证明递归&分治的方法】
b.对于访问中,存在p->x1->...->xn->q访问链的p,q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
c.对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对p,q有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
2.通过主循环,对非首个节点t执行visit
此时图中节点只有状态unvisit和visited
对t执行完visit后
a.将完成在访问前状态为unvisit节点集合中,所有对t可达节点的访问
b.对于访问中,存在p->x1->...->xn->q访问链的p,q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
c.对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对p,q有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
3.假设,p,q为在不同主循环节点visit执行过程被访问的两个节点
若p所在的主循环节点,在q所在的主循环节点之前执行
则有
p->m_nFirstVisitTime < p->m_nLastVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime
算法目标&性质的证明【算法目标正确性的证明】
易于知道在首次开始对节点p执行Visit前,图中所有节点状态均为unvisit
证明1:
通过主循环,对首个节点p执行Visit,执行完visit后,将完成所有对p可达节点的访问
设任意p可达的节点q
则必然存在路径p->x1->x2->...->xn->q
采用反证法。
假设执行完visit后,存在p可达节点q,未对其进行访问。
如果xn在visit中被访问,
则,可知对xn所有可达且未被访问节点进行访问时,会访问p,这和假设矛盾。
故假设成立下,xn也未被访问。
同理,可知,假设成立下,x(n-1),...,x1也未被访问。
但是,对p所有可达且未被访问节点进行访问时,会访问x1,这和假设矛盾。
故假设不成立。
故,证明了,对p执行visit后,会完成对所有p可达节点的访问。
证明2:
通过主循环,对首个节点p执行Visit,对于访问中,存在p->x1->...->xn->q访问链的p, q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
采用直接证明法。
对于p和x1,易于知道p->m_nFirstVisitTime < x1->m_nFirstVisitTime < x1->m_nLastVisitTime < p->m_nLastVisitTime
对于x1和x2,易于知道x1->m_nFirstVisitTime < x2->m_nFirstVisitTime < x2->m_nLastVisitTime < x1->m_nLastVisitTime
...
对于xn和q,易于知道xn->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < xn->m_nLastVisitTime
故,易于知道p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
易于知道,每次在主循环对节点x执行完visit后,
图中只会存在,状态为unvisit和visited的节点
证明3:
通过主循环,对首个节点p执行Visit,对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对q1,q2有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
证明:
采用直接证明法
结合处理过程,_nTime单调增加的特性,易于知道
证明结论成立。
证明4:
通过主循环,对非首个节点t执行visit,对t执行完visit后
将完成在访问前状态为unvisit节点集合中,所有对t可达节点的访问
证明:
可以按和证明1一致的方式证明。
证明5:
通过主循环,对非首个节点t执行visit,对t执行完visit后
对于访问中,存在p->x1->...->xn->q访问链的p, q有
p->m_nFirstVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime < p->m_nLastVisitTime
证明:
可以按和证明2一致的方式证明
证明6:
通过主循环,对非首个节点t执行visit,对t执行完visit后
对于访问中,通过不同访问链
p->x1->...->xk->y1->...->yn->q1
p->x1->...->xk->z1->...->zm->q2
假设,在xk的直接可达节点访问顺序中,
按y1,z1顺序进行节点访问
则,对q1,q2有
q1->m_nFirstVisitTime < q1->m_nLastVisitTime < q2->m_nFirstVisitTime < q2->m_nLastVisitTime
证明:
可以按和证明3一致的方式证明
证明7:
假设,p,q为在不同主循环节点visit执行过程被访问的两个节点
若p所在的主循环节点,在q所在的主循环节点之前执行
则有
p->m_nFirstVisitTime < p->m_nLastVisitTime < q->m_nFirstVisitTime < q->m_nLastVisitTime
采用直接证明
因为每次visit中_nTime均单调增加,故得证。