算法--图的深度优先搜索

图的深度优先搜索

算法性质

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均单调增加,故得证。
原创文章 134 获赞 87 访问量 6万+

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/105746973
今日推荐