用邻接表表示的无向图

用邻接表表示的无向图

图的遍历算法

深度优先算法(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;
}
posted @ 2022-01-21 13:14  予己  阅读(108)  评论(0)    收藏  举报