算法-图的广度优先搜索

图的广度优先搜索

算法性质

通过对指定源点s运行广度优先搜索,
可以分辨出图中所有从s可达的节点集合,所有从s不可达的节点集合。
对于图中所有从s可达的节点p,将得到一条s~p的最短路径信息。

接口设计

template<typename Key, typename Value>
class BreadthFirstVisit
{
public:
	class Node;
	typename typedef DataStruct::GraphStruct::Graph<Key, Value> InnerGraph;
	typename typedef InnerGraph::Node InnerGraphNode;
	typename typedef InnerGraph::Pair InnerGraphPair;
	typename typedef DataStruct::Tree::SortedBalanceBinaryTree<Key, Node*> InnerTree;
	class Node
	{
	public:
		Node* GetPreNode()
		{
			return m_pBfvPathNode;
		}

		typename DataStruct::GraphStruct::Graph<Key, Value>::Node* GetGraphNode()
		{
			return m_pGraphNode;
		}
		
		int GetDistance()
		{
			return m_nBfvPathDistance;
		}
	private:
		Node()
		{
			m_nBfvPathDistance = -1;
			m_pGraphNode = nullptr;
			m_pBfvPathNode = nullptr;
		}

		Node(InnerGraphNode* pGraphNode_)
		{
			m_pGraphNode = pGraphNode_;
			m_nBfvPathDistance = -1;
			m_pBfvPathNode = nullptr;
		}

		~Node()
		{
		}

		void Reset()
		{
			m_pBfvPathNode = nullptr;
			m_nBfvPathDistance = -1;
		}
	private:
		InnerGraphNode* m_pGraphNode;
		Node* m_pBfvPathNode;
		int m_nBfvPathDistance;

		friend class BreadthFirstVisit;
	};

	BreadthFirstVisit(const InnerGraph& nGraph_);
	~BreadthFirstVisit();

	DataStruct::Array::DynArray<Node*> Run(const Key& nSourceKey_);
private:
			BreadthFirstVisit(const BreadthFirstVisit& nBFV_) = default;
			BreadthFirstVisit& operator=(const BreadthFirstVisit& nBFV_) = default;
private:
	InnerGraph m_nGraph;
	InnerTree m_tKeyToNodeMap;
};

实现

构造

template<typename Key, typename Value>
BreadthFirstVisit<Key, Value>::BreadthFirstVisit(const InnerGraph& nGraph_)
	: m_nGraph(nGraph_)
{
	DataStruct::Array::DynArray<InnerGraphNode*> _arrpGraphNodes = m_nGraph.GetNodesArray();
	for (int _i = 0; _i < _arrpGraphNodes.GetSize(); _i++)
	{
		Node* _pNode = nullptr;
		if (_arrpGraphNodes[_i] == nullptr)
		{
			throw "graph node exception";
		}

		try
		{
			_pNode = new Node(_arrpGraphNodes[_i]);
		}
		catch (...)
		{
			_pNode = nullptr;
			throw "out of memory";
		}

		InnerTree::Pair _nPair;
		InnerGraphPair _nGraphPair = _arrpGraphNodes[_i]->GetPair();
		_nPair.m_nKey = _nGraphPair.m_nKey;
		_nPair.m_nValue = _pNode;
		m_tKeyToNodeMap.Add(_nPair);
	}
}

析构

template<typename Key, typename Value>
BreadthFirstVisit<Key, Value>::~BreadthFirstVisit()
{
	DataStruct::Array::DynArray<InnerTree::Pair> _arrpNodes = m_tKeyToNodeMap.GetArray();
	int _nSize = _arrpNodes.GetSize();
	for (int _i = 0; _i < _nSize; _i++)
	{
		delete _arrpNodes[_i].m_nValue;
		_arrpNodes[_i].m_nValue = nullptr;
	}
}

算法运行

template<typename Key, typename Value>
DataStruct::Array::DynArray<typename BreadthFirstVisit<Key, Value>::Node*> BreadthFirstVisit<Key, Value>::Run(const Key& nSourceKey_)
{
	// 数据重置
	DataStruct::Array::DynArray<InnerTree::Pair> _arrPairs = m_tKeyToNodeMap.GetArray();
	for (int _i = 0; _i < _arrPairs.GetSize(); _i++)
	{
		if (_arrPairs[_i].m_nValue == nullptr)
		{
			throw "unexpected error";
		}

		_arrPairs[_i].m_nValue->Reset();
	}
	// 1.算法目标
	// 2.正确性证明
	// 3.效率分析
	DataStruct::Queue::DynQueue<Node*> _qpNodes;
	InnerTree::Node* _pSourceNode = m_tKeyToNodeMap.Search(nSourceKey_);
	if (_pSourceNode == nullptr
		|| _pSourceNode->GetPair().m_nValue == nullptr
		|| _pSourceNode->GetPair().m_nValue->m_pGraphNode == nullptr)
	{
		throw "source node is not exist";
	}

	Node* _pNode = _pSourceNode->GetPair().m_nValue;
	_pNode->m_nBfvPathDistance = 0;
	_qpNodes.In(_pNode);
	while (_qpNodes.IsEmpty() == false)
	{
		Node* _pNode = _qpNodes.Out();
		DataStruct::Array::DynArray<Key> _arrDestinations = _pNode->m_pGraphNode->GetDests();
		int _nSize = _arrDestinations.GetSize();
		for (int _i = 0; _i < _nSize; _i++)
		{
			Node* _pRelatedNode = nullptr;
			m_tKeyToNodeMap.Search(_arrDestinations[_i], _pRelatedNode);
			if (_pRelatedNode == nullptr)
			{
				throw "unexpected error";
			}

			if (_pRelatedNode->m_nBfvPathDistance == -1)
			{
				_pRelatedNode->m_pBfvPathNode = _pNode;
				_pRelatedNode->m_nBfvPathDistance = _pNode->m_nBfvPathDistance + 1;
				_qpNodes.In(_pRelatedNode);
			}
		}
	}

	_arrPairs = m_tKeyToNodeMap.GetArray();
	DataStruct::Array::DynArray<Node*> _arrpNodes;
	for (int _i = 0; _i < _arrPairs.GetSize(); _i++)
	{
		_arrpNodes.Add(_arrPairs[_i].m_nValue);
	}

	return _arrpNodes;
}

正确性证明

循环不变式:
节点p进入队列时,
节点p是从源点s可达的,
源点s到节点p的最短路径信息【前一节点,距离】是已知的。

初始时,
源点s进入队列,
我们认为,源点s从源点s可达,
原点s到源点s的最短路径信息已知【前一节点为null,距离为0】
循环不变式成立。

第k次迭代,
依据循环不变式,
第1,...,k-1次迭代后循环不变式均成立
则,
迭代过程,从队列取出的节点q,由于q是之前放入队列的,
故依据循环不变式,
源点s到节点q的最短路径信息[前一节点,距离]是已知的

我们的迭代处理过程,
取出从q直接可达的节点p
若节点p的m_nDistance != -1 意味着,
节点p之前已经进入过队列,
故源点s到节点p的最短路径信息[前一节点,距离]是已知的,我们对其略过。
若节点p的m_nDistance == -1,
意味这,节点p之前未进入过队列,
此时,迭代过程设置p的m_nDistance和m_pPreNode,然后将p放入队列
为了证明,循环不变式在此迭代处理后,仍然满足,
我们需要证明
1.s~q-p是s~p的一条最短路径
采用反证法证明:
假设,s~q-p不是s~p的一条最短路径
由于此时我们知道p是从s可达的,
故s~p的最短路径是必然存在的。
设s~x-p为s~p的一条最短路径。
x!=q
此时我们知道,节点x在此刻尚未出过队列
因为如果节点x此前已经出过队列,
则在节点x出队列时候,
因为此时,节点p满足,
节点p通过节点x直接可达,节点p的m_nDistance为-1,
则按我们的迭代处理,
此时会设置p的m_nDistance,并将p入队列,
这和,后面处理到q时,p的m_nDistance为-1不符合。
故得证。

所以,节点x必然在节点q之后出队列,
由于队列的性质,
我们知道,节点x必然在节点q之后入的队列。
由于节点进入队列时,
其m_nDistance,是在先进入队列节点上单调增加得到的。
可知,
【先进入队列节点的m_nDistance,必然小于或等于后进入队列节点的m_nDistance。】

故q->m_nDistance <= x->m_nDistance
则,(s~x-p)->m_nDistance <= (s~q-p)->m_nDistance <= (s~x-p)->m_nDistance
(s~q-p)->m_nDistance = (s~x-p)->m_nDistance
故,(s~q-p)是s~p的一条最短路径。

综合,循环不变式成立。
依据上述循环不变式,可以证明,
对于进入队列节点,我们在进入时已经知道其最短路径信息【前一节点,距离】

证明:
对于任意从s可达节点p,节点p必然会进入队列。
反证法:
假设存在节点p,通过s可达,但节点p在迭代处理中没有进入过队列。
不妨设存在一条这样的路径s-p1-p2-....-pn-p
因为p不会进入队列,故迭代过程,节点p的m_nDistance始终为-1
可以推的,
pn永远不会进入队列, 因为pn如果进入队列,在pn出队列时,p将被加入队列。
同理,可以堆得p(n-1)....p2,p1,s不会进入队列。
与已知相违背。
假设不成立。得证。

综合,循环不变式和上述证明
得到,算法处理中,
所有s可达节点均被处理,且处理后得到s到其最短路径信息【前一节点,距离】
所有s不可达节点,不会进入队列,不会被处理。

证明:【先进入队列节点的m_nDistance,必然小于或等于后进入队列节点的m_nDistance。】
循环不变式:
任意时刻,
队列尾部节点距离,大于等于所有在其前进入过队列的节点的距离
初始化时,
队列只有一个源节点,满足循环不变式

对第k次迭代,
依据循环不变式,第1,...,k-1次迭代后,循环不变式均成立。
迭代中,新加入队列的任意节点q,本次迭代出队列节点为t1

若节点q为本次迭代,加入的第一个节点,
则加入时,
队列尾部节点p为上次迭代出队列节点t2在迭代中最后加入队列的节点。
q的距离 = t1的距离+1
p的距离 = t2的距离+1
可知,t2先于t1进入队列,
依据循环不变式有 t2的距离 <= t1的距离
可以得到,
q的距离 >= p的距离

若节点q为本次迭代,非加入的第一个节点,
则加入时,队列尾部节点p为本次迭代前面加入节点
q的距离 = t1的距离+1
p的距离 = t1的距离+1
可以得到,
q的距离 >= p的距离

故,迭代操作后,循环不变式仍然满足。
得证。
原创文章 134 获赞 87 访问量 6万+

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/105722531