图由顶点集和边集组成,记为G=(V,E)

图的存储

1、邻接矩阵。用一个一维数组存储图中的顶点信息,用一个二维数组存储边的信息。

 图的临界矩阵存储结构定义

typedef char vertexType[10];   //vertexType  a; sizeof(a) = 10;
#define MAX 50  //最大顶点数
typedef struct
{
    vertexType vertex[MAX]; //顶点数组
    int edge[MAX][MAX];//边表
    int vertexNum; //顶点个数
    int edgeNum; //边的个数
}Graph;

创建一个无向图

//求用户输入的顶点在顶点数组的位置
int LocateVertex(Graph &g, vertexType v)
{
    for (size_t i = 0; i < g.vertexNum; ++i)
    {
        if (strcmp(v, g.vertex[i]) == 0)
            return i;
    }
    return -1;
}
void  CreateGraph(Graph &g)
{
    cout << "请输入图中的顶点数和边数" << endl;
    cin >> g.vertexNum >> g.edgeNum;
    cout << "请输入" << g.vertexNum << "个顶点数的值" << endl;
    for (size_t i = 0; i < g.vertexNum; i++)
    {
        cin >> g.vertex[i];  //把输入的顶点存储在顶点数组中
    }
    //初始化矩阵为0
    for (size_t i = 0; i < g.vertexNum; i++)
    {
        for (size_t j = 0; j < g.vertexNum; j++)
        {
            g.edge[i][j] = 0;
        }
    }
    //如果v0和v1之间有边输入 v0 v1
        //如果v2和v4之间有边输入 v2 v4
    cout << "请输入" << g.edgeNum << "条边的两个顶点" << endl;
    vertexType v1, v2;  //相当于char v1[10]  char v2[10]
    for (size_t i = 0; i < g.edgeNum; i++)
    {
        cin >> v1 >> v2 ;
        //用户输入的顶点在顶点数组中的位置
        int m = LocateVertex(g, v1);
        int n = LocateVertex(g, v2);

        //边对应的二维数组赋值
        g.edge[m][n] = 1;
        g.edge[n][m] = 1;
    }

}

2、邻接表。对图中的每个顶点vi建立一条单链表,第i个单链表中的结点表示依附于顶点vi的边。这个单链表就称为顶点vi的边表。边表的头指针和顶点的数据采用顺序存储。

邻接表的存储结构

//边表
struct edgeNode{
    int position;  //当前顶点在顶点数组中的位置
    struct edgeNode * next;
};
//顶点表结构
struct vertex
{
    char name[9];
    struct edgeNode * first;  //边表头指针
};
struct GraphList
{
    vertex head[MAX];  //顶点表数组
    int vertexNum;   //顶点数
    int edgeNum;  //边数
};

创建图

void CreateGraph(GraphList &g)
{
    cout << "请输入图的顶点数和边数" << endl;
    cin >> g.vertexNum >> g.edgeNum;
    cout << "输入" << g.vertexNum << "个顶点的值" << endl;
    for (size_t i = 0; i < g.vertexNum; i++)
    {
        cin >> g.head[i].name;
        g.head[i].first = NULL;  //头指针置为空
    }
    cout << "请输入边的两个顶点" << endl;
    char v1[9], v2[9];
    for (size_t i = 0; i < g.edgeNum; i++)
    {
        cin >> v1 >> v2;
        //输入的顶点在数组中的位置
        int m = LocateVertex(g, v1);
        int n = LocateVertex(g, v2);

        //链表中添加邻接点
        edgeNode * pNew = new edgeNode;
        pNew->position = n;

        //头插法
        pNew->next = g.head[m].first;
        g.head[m].first = pNew;

#if 1
        //无向图返过来
        edgeNode * pNew1 = new edgeNode;
        pNew1->position = m;
        pNew1->next = g.head[n].first;
        g.head[n].first = pNew1;
#endif;

    }
}

2、图的遍历

1、深度优先遍历(DFS)

  类似于树的先序遍历。首先访问某一个起始位置V,由V出发,访问与V相邻但未被访问的任意一个顶点W,再访问W邻接但没有被访问的顶点......重复以上过程。当不能在访问时,依次退回最近一个被访问的顶点,如果他有邻接点但是未被访问,从这个顶点开始继续上述搜索过程。直到所有的顶点都被访问完为止。

void DFS(Graph &g)
{
        //一个标记数组,用来标记是否被访问,初始化为false,
    bool * visit = new bool[g.vertexNum];
    for (size_t i = 0; i < g.vertexNum; i++)
    {
        visit[i] = false;
    }
    stack<int> s;
    visit[0] = true; //标记
    cout << g.vertex[0] << " "; //访问
    s.push(0); //入栈
    while (!s.empty())
    {
        for (size_t i = 0; i < g.vertexNum; i++)
        {
            int top = s.top();
            if (!visit[i] && g.edge[top][i]>0)  //有邻接点,且顶点未被访问
            {
                visit[i] = true;  //标记
                cout << g.vertex[i] << " "; //访问
                s.push(i); //入栈
            }
        }
        s.pop();  //不能继续访问,退回到最近访问的顶点
    }
    delete[] visit;
}

2、广度优先遍历(BFS)

  访问起始顶点V,由V出发访问V的各个未被访问的所有邻接点w1,w2,w3.......,然后访问w1,w2,w3....的所有未被访问的顶点,依次类推。

void BFS(Graph &g)
{
    bool *visit = new bool[g.vertexNum];
    for (size_t i = 0; i < g.vertexNum; i++)
    {
        visit[i] = false;
    }
    queue<int>q;
    visit[0] = true;
    cout << g.vertex[0] << " ";
    q.push(0);  //入队
    while (!q.empty())
    {
        int front = q.front();
        for (size_t i = 0; i < g.vertexNum; i++)
        {
            if (!visit[i] && g.edge[front][i]>0) //访问与该顶点邻接但未被访问的所有顶点
            {
                visit[i] = true;
                cout << g.vertex[i] << " ";
                q.push(i);
            }
        }
        q.pop(); //出队
    }
    delete[] visit;
}

3、图的应用

最短路径问题(迪杰斯特拉算法)

当图是带权图时,把从一个顶点v0到图中任意一个顶点vi的一个路径,可能不止一条,所经过边上的权值之和,定义为该路径的带权路径长度,其中最短的那条路径为最短路径。

该算法需要三个辅助数组,S[]记录已经求得的最短路径的顶点,dist[]记录从源点到其余各点的当前最短路径长度,path[]:path[i]表示到顶点i的最短路径的前驱结点。

步骤:1、初始化集合S[]为0,当有一个顶点计算完成,将对应的下标的值改为1;

  2、初始化数组dist[],记录从源点到其他各个顶点当前的最短路径长度,从中选出最小的那个j,将j加入到S集合中。

  3、修改从源点出发到任意一个顶点vk可达的最短路径长度:如果dist[j]+arcs[j][k]<dist[k]则令dist[k]=dist[j]+arcs[j][k]

  4、重复以上动作

 

 

int * Dijkstra(Graph g, int v)
{
    //==================初始化=====================
    int s[MAXVERTEX];//标记数组,表示当前有哪些顶点加入
    int path[MAXVERTEX];//最短路径中顶点的前驱顶点
    int dis[MAXVERTEX];//从原点出发,到各个顶点的最短路径
    for (int i = 0; i < g.vertexNum; ++i)
    {
        dis[i] = g.edge[v][i];
        s[i] = 0;
        if (g.edge[v][i] < MAXNUM)
            path[i] = v;
        else
        {
            path[i] = -1;
        }
    }
    path[v] = -1;
    s[v] = 1;  //原点加入
    //============初始化完成========================
    for (int i = 0; i < g.vertexNum; ++i)
    {
        //找dis数组中最小的值
        int min = MAXNUM;
        int u;//记录最小值的顶点
        for (size_t j = 0; j < g.vertexNum; j++)
        {
            if (dis[j] < min)
            {
                min = dis[j];
                u = j;
            }
        }
        s[u] = 1;  //加入进来
        for (int j = 0; j < g.vertexNum; ++j)
        {
            if (s[j] == 0 && dis[u] + g.vertex[u][j] < dis[j])
            {
                dis[j] = dis[u] + g.vertex[u][j];
                path[j] = u;  //修改path表明到j顶点的前一个顶点是u
            }
        }
    }
    return path;
    
}
void showpath(Graph &g, int *path, int v0, int v)
{
    stack<int>s;
    int temp = v;
    while (temp!=v0)
    {
        s.push(temp);
        temp = path[temp];
    }
    while (!s.empty())
    {
        cout << g.vertex[s.top()] << "--->";
        s.pop();
    }
}