最短路径算法

        最短路径 是数学中的一类问题,常常在 奥数比赛、计算机竞赛等场合出没,对应现实生活的问题就是 找一个从A点到B点的最短路,其价值和意义很明显。

        常用的路径算法有:Dijkstra算法、A*算法、SPFA算法、动态规划法、Bellman-Ford算法和Floyd-Warshall算法 等等,这里我们只讲 最常用的Dijkstra算法。

Dijkstra算法:

        算法采用了贪心思想,即每次都查找与该点距离最近的点,其算法复杂度为 O(n^2)。

        定义S描述已完成规划点集,U表示未完成规划点集,v表示源顶点,那么算法步骤可以描述为:

1. 初始化,S只含有源顶点v;

2. 从U中选取一个距离v最小的顶点k加入S中(该选定的距离就是v到k的最短路径长度);

3. 以k为新考虑的中间点,修改U中各顶点的距离;若从源节点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值是顶点k的距离加上k到u的距离;

4. 重复步骤 2、3,直到所有顶点都包含在S中。


代码参考:

        头文件(SearchGraph.h)

/* SearchGraph类 - 最短路径搜索 
    linolzhang, 2009.6.22
*/
#include <vector>
#include <string>
#include <map>
#include <queue>
#include <functional>
#include <float.h>

// 边
struct Vertex;
struct Edge
{
	Vertex* src;    // s
	Vertex* dest;   // w
	float weight;

	Edge(Vertex* _src,Vertex* _dest,float w) : src(_src),dest(_dest),weight(w)
	{
	}
};
struct EdgeIndex  // 用于边索引查找
{
	std::string srcID;
	std::string destID;

	EdgeIndex(const std::string& _srcID,const std::string& _destID) : srcID(_srcID),destID(_destID)
	{
	}
	bool operator < (const EdgeIndex& rhs) const
	{  
		if(srcID != rhs.srcID)
			return srcID < rhs.srcID;

		return destID < rhs.destID;
	}
};
struct QNode  // 用于搜索
{   
	Vertex* vertex;
	float dist;

	QNode(Vertex* _vertex=NULL,float _dist=0.0f) : vertex(_vertex),dist(_dist)
	{
	}
	bool operator < (const QNode& rhs) const  {  return dist<rhs.dist; }
	bool operator > (const QNode& rhs) const  {  return dist>rhs.dist; }
};

// 每个顶点基本信息
struct Vertex
{
	std::string	ID;      // 顶点ID
	std::vector<Edge*> adjEdgeInList;   // 入边列表
	std::vector<Edge*> adjEdgeOutList;  // 出边列表

	float dist;    // 与目标点的最短距离
	Vertex* next;  // 记录到目标最短路径中的 下一个顶点
	bool bVisited; // 是否已访问

	Vertex(const std::string& id) : ID(id),dist(FLT_MAX),next(NULL),bVisited(false)
	{
	};

	void reset()
	{ 
		dist = FLT_MAX; next = NULL; bVisited = false;
	}
};

// 搜索图类,记录有向边集合,Djkstra算法搜索最短路径
class SearchGraph
{
public:
	SearchGraph(void);
	~SearchGraph(void);

	SearchGraph* clone();        // 克隆一个搜索图

public:
	Vertex* addVertex(const std::string& id);   // 添加顶点
	Vertex* getVertex(const std::string& id);   // 得到顶点
	void removeVertex(const std::string& id);   // 删除顶点,需要先删除其关联的边

	bool addEdge(const std::string& srcID,const std::string& destID,float weight);  // 添加边
	Edge* getEdge(const std::string& srcID,const std::string& destID);              // 得到边
	void removeEdge(const std::string& srcID,const std::string& destID);            // 删除边,首先解除与顶点的关联

	// djkstra算法搜索最短路
	bool getShortestPath(const std::string& vID,bool bFrom=true);    // bFrom :从该点出发

protected:
	std::map<std::string,Vertex*> m_VertexList;   // 顶点列表
	std::map<EdgeIndex,Edge*> m_EdgeList;         // 边列表
};

        cpp文件(SearchGraph.cpp)

#include "SearchGraph.h"

SearchGraph::SearchGraph(void)
{
}

SearchGraph::~SearchGraph(void)
{
	// 删除顶点
	std::map<std::string,Vertex*>::iterator ite_v = m_VertexList.begin();
	for( ; ite_v != m_VertexList.end(); ++ite_v )
		delete ite_v->second;

	// 删除边
	std::map<EdgeIndex,Edge*>::iterator ite_e = m_EdgeList.begin();
	for( ; ite_e != m_EdgeList.end(); ++ite_e )
		delete ite_e->second;
}

// 克隆一个搜索图
SearchGraph* SearchGraph::clone()
{
	SearchGraph* newSearchGraph = new SearchGraph();

	// 复制Vertex
	std::map<std::string,Vertex*>::iterator ite_v = m_VertexList.begin();
	for( ; ite_v != m_VertexList.end(); ++ite_v )
	{
		newSearchGraph->addVertex(ite_v->first);
	}

	// 复制Edge
	std::map<EdgeIndex,Edge*>::iterator ite_e = m_EdgeList.begin();
	for( ; ite_e != m_EdgeList.end(); ++ite_e )
	{
		newSearchGraph->addEdge(ite_e->first.srcID,ite_e->first.destID,ite_e->second->weight);
	}

	return newSearchGraph;
}

/
// --------------------------------------------
// 添加顶点
Vertex* SearchGraph::addVertex(const std::string& id)
{
	// 首先查找顶点ID,是否存在
	std::map<std::string,Vertex*>::iterator ite = m_VertexList.find(id);
	if( ite != m_VertexList.end() )
	{
		Vertex* pVertex = ite->second;
		return pVertex;
	}
	else
	{
		Vertex* pVertex = new Vertex(id);
		m_VertexList[id] = pVertex;
		return pVertex;
	}
}
// 得到顶点
Vertex* SearchGraph::getVertex(const std::string& id)
{
	// 查找顶点
	std::map<std::string,Vertex*>::iterator ite = m_VertexList.find(id);
	if( ite != m_VertexList.end() )
	{
		Vertex* pVertex = ite->second;
		return pVertex;
	}
	return NULL;
}
// 删除顶点,需要先删除其关联的边
void SearchGraph::removeVertex(const std::string& id)
{
	// 首先查找顶点ID,是否存在
	std::map<std::string,Vertex*>::iterator ite = m_VertexList.find(id);
	if( ite != m_VertexList.end() )
	{
		Vertex* pVertex = ite->second;
		// 首先删除关联的边
		for(size_t i=0;i<pVertex->adjEdgeInList.size();i++)
		{
			removeEdge(pVertex->adjEdgeInList[i]->src->ID,pVertex->adjEdgeInList[i]->dest->ID);
		}
		for(size_t i=0;i<pVertex->adjEdgeOutList.size();i++)
		{
			removeEdge(pVertex->adjEdgeOutList[i]->src->ID,pVertex->adjEdgeOutList[i]->dest->ID);
		}
		// 删除顶点
		delete pVertex;
		m_VertexList.erase(ite);
	}
}

bool SearchGraph::addEdge(const std::string& srcID,const std::string& destID,float weight)
{
	Vertex* pSrc = addVertex(srcID);
	Vertex* pDest = addVertex(destID);

	// 查找是否已添加
	Edge* pEdge = getEdge(srcID,destID);
	if(pEdge)
		return false;

	// 建立新边
	pEdge = new Edge(pSrc,pDest,weight);
	m_EdgeList.insert( std::pair<EdgeIndex,Edge*>(EdgeIndex(srcID,destID),pEdge) );

	pSrc->adjEdgeOutList.push_back( pEdge );
	pDest->adjEdgeInList.push_back( pEdge );

	return true;
}

// 得到边
Edge* SearchGraph::getEdge(const std::string& srcID,const std::string& destID)
{
	EdgeIndex index(srcID,destID);

	// 查找边
	std::map<EdgeIndex,Edge*>::iterator ite = m_EdgeList.find(index);
	if( ite != m_EdgeList.end() )
	{
		Edge* pEdge = ite->second;
		return pEdge;
	}
	return NULL;
}
// 删除边,首先解除与顶点的关联
void SearchGraph::removeEdge(const std::string& srcID,const std::string& destID)
{
	EdgeIndex index(srcID,destID);

	// 查找边
	std::map<EdgeIndex,Edge*>::iterator ite = m_EdgeList.find(index);
	if( ite != m_EdgeList.end() )
	{
		Edge* pEdge = ite->second;

		// 首先解除与顶点的关联
		std::vector<Edge*>::iterator ite1 = pEdge->src->adjEdgeOutList.begin();
		for( ; ite1!=pEdge->src->adjEdgeOutList.end(); ++ite1 )
		{
			if( *ite1 == pEdge )
				pEdge->src->adjEdgeOutList.erase(ite1);  // 移除邻接边 - 出边
		}
		std::vector<Edge*>::iterator ite2 = pEdge->dest->adjEdgeInList.begin();
		for( ; ite2!=pEdge->dest->adjEdgeInList.end(); ++ite2 )
		{
			if( *ite2 == pEdge )
				pEdge->dest->adjEdgeInList.erase(ite2);  // 移除邻接边 - 入边
		}

		// 删除边
		delete pEdge;
		m_EdgeList.erase(ite);
	}
}

// djkstra算法搜索最短路 - bFrom:从该点出发
bool SearchGraph::getShortestPath(const std::string& vID,bool bFrom)
{
	// 顶点是否存在 ?
	std::map<std::string,Vertex*>::iterator ite = m_VertexList.find(vID);
	if( ite == m_VertexList.end() )
		return false;

	Vertex* startVertex = ite->second;

	//  重置搜索图,清空标记
	ite = m_VertexList.begin();
	for( ; ite!=m_VertexList.end(); ++ite )
		ite->second->reset();

	// 定义优先队列,greater最小堆 - 默认是最大堆less
	std::priority_queue< QNode,std::vector<QNode>,std::greater<QNode> > q;

	// 初始化,添加启始搜索顶点
	startVertex->dist = 0;
	QNode startNode(startVertex,0);  // 初始节点,dist置0
	q.push(startNode);

	// ------------------------------------------
	// 遍历
	QNode currNode;    // 当前遍历到的节点
	while(!q.empty())
	{
		// 弹出栈顶最大 Dist 的节点
		currNode = q.top();  q.pop();

		// 过滤已访问过的情况
		if(currNode.vertex->bVisited)
			continue;
		currNode.vertex->bVisited = true;

		// 搜索方向控制
		size_t adjEdgeCount = 0;  // 邻接边数
		if(bFrom)  // 搜索点作为起始点
			adjEdgeCount = currNode.vertex->adjEdgeOutList.size();
		else       // 搜索点作为终止点
			adjEdgeCount = currNode.vertex->adjEdgeInList.size();

		// 遍历邻接边
		for( size_t i=0; i<adjEdgeCount; i++ )
		{
			Edge* pEdge;
			Vertex* pNextVertex;
			if(bFrom)
			{
				pEdge = currNode.vertex->adjEdgeOutList[i];  // 出边
				pNextVertex = pEdge->dest;
			}
			else
			{
				pEdge = currNode.vertex->adjEdgeInList[i];   // 入边
				pNextVertex = pEdge->src;
			}
			// 过滤已访问过的情况 - 不加也可以,通过距离判断
			if(pNextVertex->bVisited)
				continue;
	
			// 如果 目标顶点 到 开始顶点 的距离 大于
			// 以 当前节点 为中介的路径长度(当前节点dist+边weight),需要更新最短路
			if( pNextVertex->dist > currNode.dist+pEdge->weight )
			{
				pNextVertex->dist = currNode.dist + pEdge->weight;
				pNextVertex->next = currNode.vertex;  // 记录前驱
				q.push( QNode(pNextVertex,pNextVertex->dist) );
			}
		}
	}

	// 搜索完成,返回
	return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值