用邻接表表示的无向图
用邻接表表示的无向图
图的遍历算法
深度优先算法(DFS)
即类似于二叉树的前序遍历,不同点在于由于图的关系是一对多的,因此需要一个visit[vertexNum]数组用来表示该点是否被遍历过
核心在于1.需要向迭代函数DFS(int)传入所有的未被遍历到的顶点(因为图有可能是非连通图,连通图仅传入一个即可),而在二叉树中只需要传入头指针指向的顶点即可,2.在迭代函数中需要将其所有未被遍历过的邻接顶点传入迭代函数,而在二叉树中只需要传入其左右孩子节点即可
广度优先算法(BFS)
即类似于二叉树的层序遍历,其核心点和深度优先算法一致!
最小生成树算法
Prim 算法
Prim 算法本质:从一个节点开始,逐步找每个节点的权值最小的边
Prim算法需要两个数组:
- lowcost[vertexNum]的值用来指示每一轮循环的权值,其中当值为0时说明该索引对应的节点已经在最小生成树中,下次跳过
- adjvex[vertexNum]的值用来指示每一轮循环对应的索引处节点的前缀
Kruskal算法
Kruskal算法本质:将各边的权值按从小到大排列,从最小的权值的边开始遍历,分别判断每条边即对应的两个顶点是否在若干个最小生成树中,从而判断选取该边还是跳过该边继续遍历
Kruskal算法需要一个数组:
- parent[vertexNum]的值用来表示整个连通图的各个最小生成树集合,每次即通过函数检测两个节点是否在其中的一个集合内!
最短路径算法
Dijkstra算法
核心:依次寻找源点到连通图中每一个顶点的最短路径
Dijkstra算法需要三个数组:
- shortPaths[vertexNum]的值用来指示源点V0到其余顶点的最短路径值
- nodes[vertexNum]的值用来指示每一个顶点V的最短路径中的前缀顶点
- final[vertexNum]的值表示V0到该点的最短路径是否寻找到,1为寻找到
整个代码如下:
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<fstream>
#include<stack>
using namespace std;
struct Edge
{
Edge(int b,int e,int w):begin(b),end(e),weight(w){}
int begin;
int end;
int weight;
};
struct EdgeNode
{
EdgeNode(int AdjPos,int Weight):adjPos(AdjPos),weight(Weight),next(nullptr){}
EdgeNode():adjPos(0),weight(0),next(nullptr){}
int adjPos;
int weight;
EdgeNode *next;
};
struct VerTexNode
{
VerTexNode(char datas):data(datas){}
char data;
EdgeNode *firstEdge = nullptr;
EdgeNode *lastEdge = nullptr;//尾插法
};
class ALGraph
{
public:
ALGraph(istream&,int nums);
void DFSerach();
void BFSerach();
void miniSpanTree_Prim() const;
void miniSpanTree_Kruskal() const;
void shortestPath_Dijkstra(int,int) const;
private:
vector<VerTexNode*> verTex;
vector<int> visited;
void DFS(int i);
int IncMiniSpanTree(const vector<int>&, int) const;
const static int MAXINDEX;
};
const int ALGraph::MAXINDEX = 65536;
ALGraph::ALGraph(istream& in,int nums)
{
size_t i, j;
char datas;
int cnt=0;
while (in>>datas)
{
verTex.push_back(new VerTexNode(datas));
if(++cnt>=nums)
break;
}
visited = vector<int>(nums);
int weights;
while (in>>i>>j>>weights)
{
auto curEdgeNode = new EdgeNode(j, weights);
if(!verTex[i]->firstEdge)
{
verTex[i]->firstEdge = curEdgeNode;
verTex[i]->lastEdge = curEdgeNode;
}
else
{
verTex[i]->lastEdge->next = curEdgeNode;
verTex[i]->lastEdge = curEdgeNode;
}
auto curEdgeNode1 = new EdgeNode(i, weights);
if(!verTex[j]->firstEdge)
{
verTex[j]->firstEdge = curEdgeNode1;
verTex[j]->lastEdge = curEdgeNode1;
}
else
{
verTex[j]->lastEdge->next = curEdgeNode1;
verTex[j]->lastEdge = curEdgeNode1;
}
}
}
void ALGraph::DFSerach()
{
int size = verTex.size();
for (auto i = 0; i < size; ++i)
{
if(!visited[i])
DFS(i);
}
cout << endl;
}
void ALGraph::DFS(int i)
{
visited[i] = 1;
cout << verTex[i]->data << " ";
auto p = verTex[i]->firstEdge;
while (p)
{
if(!visited[p->adjPos])
DFS(p->adjPos);
p = p->next;
}
}
void ALGraph::BFSerach()
{
int size = verTex.size();
for (int i = 0; i < size;++i)
{
visited[i] = 0;
}
queue<int> Q;
for (int i = 0; i < size;++i)
{
if(!visited[i])
{
Q.push(i);
visited[i] = 1;
while(!Q.empty())
{
auto current = Q.front();
cout << verTex[current]->data << " ";
auto p = verTex[current]->firstEdge;
while (p)
{
if(!visited[p->adjPos])
{
visited[p->adjPos] = 1;
Q.push(p->adjPos);
}
p = p->next;
}
Q.pop();
}
}
}
cout << endl;
}
void ALGraph::miniSpanTree_Prim() const//默认从第一个节点开始,这里假定整张图是连通图,并没有进行连通图检验
{
int size = verTex.size();
vector<int> lowcost(size, MAXINDEX);//表示不同节点相对于其他节点的权值,若某个节点已经被选中作为最小生成树则对应位置为0
vector<int> adjvex(size, 0);//表示每次的边(i,j)中的i到底是哪个最小生成树中的节点,j即是lowcost中的索引
int Min = MAXINDEX;//代表每次节点更小的权值
int k = 0;//代表每次选择的要写入最小生成树中的节点
lowcost[0] = 0;
auto p = verTex[0]->firstEdge;
while(p)
{
lowcost[p->adjPos] = p->weight;
p = p->next;
}
//因为找n-1条边,因此大的循环应该是n-1次
for (int i = 1; i < size;++i)
{
Min = MAXINDEX;
int j = 1;
while(j < size)
{
if(lowcost[j] && lowcost[j]<Min)
{
k = j;
Min = lowcost[j];
}
++j;
}
lowcost[k] = 0;
cout << "( " << adjvex[k] << ", " << k << ") 的权值为: " << Min << endl;
auto p = verTex[k]->firstEdge;
while (p)
{
if(lowcost[p->adjPos] && p->weight<lowcost[p->adjPos])
{
lowcost[p->adjPos] = p->weight;
adjvex[p->adjPos] = k;
}
p = p->next;
}
}
}
void ALGraph::miniSpanTree_Kruskal() const
{
int size = verTex.size();
vector<Edge> edges;
for (int i = 0; i < size;++i)
{
auto p = verTex[i]->firstEdge;
while(p)
{
auto iter = find_if(edges.cbegin(), edges.cend(), [=](const Edge& e1) -> bool
{
if (i == e1.begin && p->adjPos == e1.end)
return true;
if(i==e1.end && p->adjPos == e1.begin)
return true;
return false;
});
if(iter==edges.end())
edges.push_back(Edge(i, p->adjPos, p->weight));
p = p->next;
}
}
sort(edges.begin(), edges.end(), [](Edge &e1, Edge &e2)
{ return e1.weight < e2.weight; });
int Esize = edges.size();
vector<int> parent(size);
for (int i = 0; i < Esize;++i)
{
int m = IncMiniSpanTree(parent, edges[i].begin);
int n = IncMiniSpanTree(parent, edges[i].end);
if(m!=n)
{
parent[m] = n;
cout << "( " << edges[i].begin << "," << edges[i].end << " )"
<< " weights: " << edges[i].weight << endl;
}
}
}
int ALGraph::IncMiniSpanTree(const vector<int>& v, int f) const
{
while(v[f]>0)
f = v[f];
return f;
}
void ALGraph::shortestPath_Dijkstra(int vb,int ve) const
{
int vertexNum = verTex.size();
vector<int> nodes(vertexNum, -1);
vector<int> shortPaths(vertexNum, MAXINDEX);
vector<int> final(vertexNum);
shortPaths[vb] = 0;
final[vb] = 1;
auto p = verTex[vb]->firstEdge;
while(p)
{
shortPaths[p->adjPos] = p->weight;
p = p->next;
}
for (int i = 1; i < vertexNum; ++i)
{
int min = MAXINDEX;
int k = 0;
for (int j = 0; j < vertexNum;++j)
{
if (!final[j] && shortPaths[j] < min)
{
min = shortPaths[j];
k = j;
}
}
final[k] = 1;
auto p1 = verTex[k]->firstEdge;
while(p1)
{
if(!final[p1->adjPos] && min+p1->weight < shortPaths[p1->adjPos])
{
shortPaths[p1->adjPos] = min + p1->weight;
nodes[p1->adjPos] = k;
}
p1 = p1->next;
}
}
cout << vb << " to " << ve << " shortest path: " << vb;
stack<int> S;
int current = nodes[ve];
while(current!=-1)
{
S.push(current);
current = nodes[current];
}
while(!S.empty())
{
auto top = S.top();
cout << "-->"<< top;
S.pop();
}
cout << "-->" << ve << endl;
}

浙公网安备 33010602011771号