为了简化实现,我这里的权值默认都是1并且都是有向图,不是1的话,比较费劲,还没想到咋写
因为默认权值都是1,所以BFS出来的第一个要找的末尾节点,的队列中就包含了最短路径,
此时只需要从后往前遍历,找出前驱节点就万事大吉,因为我Node里面vertex1就保存了前驱节点,更具体的代码实现如下
// 链表测试.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<list>
#include<vector>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
struct Node {
public:
int vertex1;//记录了该节点的前驱节点
int vertex2;//vertex2是该节点
Node(int v1, int v2) :vertex1(v1), vertex2(v2) {}
friend ostream& operator<<(ostream& os, const Node& x) {
cout << "<" << x.vertex1 << "->" << x.vertex2 << "> ";
return os;
}
};
struct Adj {
public:
int vertex;//该节点
list<Node> head;//STL的链表
Adj(int v = 0) :vertex(v), head() {}
bool operator==(const Adj& a1) {
return a1.vertex == vertex;
}
};
queue<Node> q;//BFS用到的队列
vector<Node> AllPath;//存路径的顶点
vector<int> path(1);//没用,忘记删了
vector<Adj> adjacnetList(1);//邻接表
int Start, End;//起始顶点,末尾顶点
void init();//初始化邻接表
void BFSMinDistance();//完成BFS的函数
void printPath();//输出路径
int main()
{
try
{
init();
BFSMinDistance();
}
catch (const std::exception& e)
{
cout << e.what();
}
}
//建立邻接表的函数
void init()
{
int n, m, a, b;
cout << "输入顶点数,边数" << endl;
cin >> n >> m;
Adj tmp(0);
list<Node> L;
cout << "输入顶点和顶点" << endl;
for (int i = 1; i <= m; ++i) {
cin >> a >> b;
auto it = find(adjacnetList.begin(), adjacnetList.end(), Adj(a));//返回指向这个元素的迭代器
if (it == adjacnetList.end()) {
adjacnetList.push_back(Adj(a));
auto iter = find(adjacnetList.begin(), adjacnetList.end(), Adj(a));
iter->head.push_front(Node(a, b));
}
else {
it->head.push_front(Node(a, b));//添加节点到邻接节点的链表域上
}
}
cout << "输入起始点和终点\n";
cin >> Start >> End;
}
void BFSMinDistance() {
try {//Node的第一个参数是前驱,第二个参数是下个顶点
q.push(Node(0, Start));
while (!q.empty()) {
auto iterator = find(adjacnetList.begin(), adjacnetList.end(), Adj(q.front().vertex2));
if (iterator == adjacnetList.end()) {//没找着
q.pop();
continue;
}
AllPath.push_back(q.front());
q.pop();
//遍历其所有的出边
for (auto ptrNode = iterator->head.begin(); ptrNode != iterator->head.end(); ++ptrNode) {
//如果找到就压入路径容器中
if (ptrNode->vertex2 == End) {
AllPath.push_back(*ptrNode);
goto End;
}
else {//没找着就压入队列
q.push(*ptrNode);
}
}
}
End:
printPath();
}
/*
6 8
1 3
1 2
2 4
2 6
3 4
3 5
4 5
6 5
1 5
*/
catch (std::exception& e) {
cout<<e.what();
cout << "异常!!\n";
}
}
//从后往前推导出最短路径,由于是从后往前,所以用到了一个栈来倒回去
void printPath()
{
stack<Node> s;
s.push(AllPath.back());
Node x = AllPath.back();
auto it = AllPath.rbegin();
++it;
while (it != AllPath.rend()) {//如果前面的元素的下一节点是栈顶的元素,则一定是最短路径的元素,压栈
if (it->vertex2 == s.top().vertex1) {
s.push(*it);
}
++it;
}
s.pop();//把节点的自环去掉,因为最开始的时候为了让队列有元素就自己添加了自环,输出路径的时候要先弹走
while (!s.empty()) {
cout << s.top();
s.pop();
}
}
/*
6 7
1 2
1 3
2 4
3 4
4 6
4 5
5 6
1 6
*/