算法之图(一)图论


图是一个多对多关系的集合,是非线性数据结构。
图由顶点(Vertex)的集合和边(Edge)的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。

一、图的基本概念

1.有向图和无向图

无向图:每条边都没有方向。
有向图:每条边都有方向。

2.顶点的度

对于无向图,顶点的度表示以该顶点作为一个端点的边的数目。
对于有向图,顶点分为入度和出度。入度表示以该顶点为终点的入边数目,出度是以该顶点为起点的出边数目,该顶点的度等于其入度和出度之和。

3.完全图

无向完全图:具有n(n-1)/2条边的无向图
有向完全图:具有n(n-1)条边的有向图。
稠密图:一个边数接近完全图的图。
稀疏图:一个边数远远少于完全图的图。

在这里插入图片描述

4.连通图

连通图:图G中任意两个顶点U,V之间存在一条从U通过若干边,点到达V的通路,则G是连通的。
强连通分量:有向图中任意两个点都连通的最大子图。
下图的强连通分量:1-2-3,4,5
在这里插入图片描述

二、图的邻接矩阵存储

邻接矩阵存储,就是用矩阵表示图中各顶点之间的邻接关系和权值。通常采用二维数组存储。
在这里插入图片描述
如下图:
在这里插入图片描述
M条边,N个顶点:

#include<iostream>
using namespace std;
// const int N = 5;
// const int M = 4;
const int oo = 0x7f;
int G[101][101];
int main(){
    //邻接矩阵存储
    int N,M;
    cin >> N >> M;
    for(int i = 1; i <= N; i++){
        for(int j = 1; j <= N; j++){
            G[i][j] = oo; //初始为无穷大;
        }
    }
    for(int k = 1; k <= M; k++){
        int x,y,w;
        cin >> x >> y >> w;//读入顶点和边权
        G[x][y] = w; //存储从点x到点y的边权
        G[y][x] = w;//无向图需要
    }
    //邻接矩阵访问
    for(int i = 1; i <= N; i++){
        for(int j = 1; j <= N; j++){
           if(G[i][j] < oo){
               cout << i <<" "<< j <<" "<< G[i][j] << endl;
           }
        }
    }
}

在这里插入图片描述

三、图的邻接表存储❤️❤️❤️(常用)

邻接表存储又叫链式存储。通常不用链表实现,用数组模拟即可
一个有向图有N个顶点,M条边。
编号计数器num:出边的编号计数器,1≤num≤M
表头数组head[i]:存储顶点i的当前出边的编号num,1<=i<=N;
终点数组to[num]:存储num号边的终点,即顶点i的邻接点。
边权数组W[num]:存储num号边的权值。
指针数组:next[num]:存储num号边的下一条边。
在这里插入图片描述
在这里插入图片描述
当顶点很多时,邻接矩阵寻找某个顶点的邻接点效率较低,若为顶点很多的稀疏图,邻接表比较好。
代码实现:
❤️❤️❤️
在这里插入图片描述
与单链表的头插法相似

#include<bits/stdc++.h>
using namespace std;
int num = 0;
int head[21]; //表头数组
int to[51]; //当前边的终点
int W[51]; //当前边的权值
int next[51]; //下一条边的编号
// struct info{
//     int next;
//     int to;
//     int w;
// };
// info f[66];
void AddEdge(int v1,int v2,int w){
    ++num;
    next[num] = head[v1];
    to[num] = v2;
    W[num] = w;   
    head[v1] = num;
}
int main(){
    int N,M;
    cin >> N >> M;
    for(int i = 0; i < M; i++){
        int x,y,w;
        cin >> x >> y >> w;
        AddEdge(x,y,w);
    }
    for(int i = head[1];i != 0;i = next[i]){
    cout << 1 <<" " << to[i] <<" "<< W[i] << endl;
    } 
}

可以将to[],next[],W[]修改成结构体
结构体邻接表

#include <bits/stdc++.h>
using namespace std;
int num = 0;
int head[21]; //表头数组
int to[51];   //当前边的终点
int w[51];    //当前边的权值
int next[51]; //下一条边的编号
struct info{
    int next;
    int to;
    int w;
};
info f[66];
void AddEdge(int v1, int v2, int w)
{
    ++num;
    f[num].next = head[v1];
    f[num].to = v2;
    f[num].w = w;
    head[v1] = num;
}
int main()
{
    int N, M;
    cin >> N >> M;
    for (int i = 1; i <= M; i++)
    {
        int x, y, w;
        cin >> x >> y >> w;
        AddEdge(x, y, w);
    }
    for (int i = head[1]; i != 0; i = f[i].next)
    {
        cout << 1 << " " << f[i].to << " " << f[i].w << endl;
    }
}

四、图的邻接点存储

一个有向图有N个顶点,M条边。

计数数组num[i]:存储顶点i邻接点个数
邻接点数组A[i][k]:存储顶点i的邻接点;
权值数组W[i][A[i][k]]: 存储点i到点A[i][k]之间的边权。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
int num[61];   // 存储邻接点个数
int A[61][61]; //存储邻接点
int W[61][61]; //存储边权
int main()
{
    int i, j, x, y, w;
    int N, M;
    cin >> N >> M;
    for (int i = 1; i <= M; i++)
    {
        cin >> x >> y >> w;
        A[x][++num[x]] = y;
        W[x][y] = w;
	  //A[y][++num[y]] = x; 无向图需要
      //W[y][x] = w;
    }
    // 邻接点访问
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= num[i]; j++)
            // cout << i  << A[i][j] << W[i][A[i][j]] ;
            cout << i << " " << A[i][j] << " " << W[i][A[i][j]] << endl;
}
/*
4 6
1 2 10
1 4 11
2 3 8
2 4 12
1 3 7
3 4 9
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

释怀°Believe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值