以入门基础理解最短路算法#2 BFS 与 DFS 、存图、STL(最短路算法必要基础) Part 2

这里是第二部分
上一部分是BFS与DFS

如何利用STL

这部分的内容因为是在边写最短路全篇时边加的,所以知识点会有点零碎。
(后期可能也会有补充)

vector

头文件
#include< vector >

用法很简单,
若a是一个vector,

a.size()                     //可以读取它的大小,
a.resize()                  //改变大小,
a.push_back()          //向尾部添加元素,
a.pop_back()            //删除最后一个元素
a.clear()                    //清空
a.empty()                 //是否为空
vector< int >a; // 一个长度可变的数组
vector<vector< int> >a;//一个长度可变的二维数组
vector<int>a[10005];//10005个长度可变的数组

并且vector支持对二维数组的随机访问和每一个长度可变的数组的单独操作。

pair

Pair C++内置的结构体

pair<int, int>a 

简单来说就是a结构体有 int 类型的变量first 和int类型的变量second。
a.first 
a.second

目前求最短路只需要知道这些

至此,STL知识结束。


存图的方法

存图方法的概述

前面讲到

写最短路问题时要考虑:
1.是否有权
2.是有向图还是无向图
3.是否有负环

存图时就需要考虑这三点。

存图的常见方法有三种:
1. 邻接矩阵
2. 邻接表
3. 前向星

所以存图又可以分成(3*3)种情况
要了解存图的基本情况,从无向无权的图开始展开讲解。

给一张小镇的图,有信息每个地方的编号,哪些地方连着,一般是这样输入。
7 6 // 顶点数是n==7和边数m==6.下面m行表示m条边
1 2 //编号为1和编号为2的地点相邻
2 4
1 3
2 5
3 7
3 6
那么如何存这张小镇的地图呢?

示例解析:

7个顶点 编号从1到7
6条边
1点和2点 相连,2点和4点相连 等等。

邻接矩阵

简单的二维矩阵
对于该题来说:可以画出这样的邻接矩阵。
在这里插入图片描述

int g[maxn][maxn]; // g[u][v]==w 表示u到v有一条权重为w的有向边

无权 无向图时

g[u][v] // g[u][v]==1时 表示u到v有一条有向边
        // g[u][v]==0时 无法从u点走到v点
//并在读边时,用g[u][v]=g[v][u]=1; 表示u点与v点有一条无向边

code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e4;
int g[maxn][maxn]; // g[u][v]==1 表示u到v有一条有向边
int n,m;
void AdjacencyMatrix(){
    int u,v;
    memset(g,0,sizeof(g));//初始化 无边
    for(int i = 0;i < m;i++){ // 读入边
        scanf("%d %d",&u,&v);
        g[u][v] = g[v][u] = 1;//因为是无向图 所以用俩个有向边表示无向边
    }
}
int main()
{
    cin>>n>>m;
    AdjacencyMatrix();
    return 0;
}

有权的处理方法

就是把

    for(int i = 0;i < m;i++){ // 读入边
        scanf("%d %d",&u,&v);
        g[u][v] = g[v][u] = 1;//因为是无向图 所以用俩个有向边表示无向边
    }
这部分中的g[u][v] = g[v][u] = 1;改成 g[u][v] = g[v][u] = w;
g[u][v] = w // w != 0时 表示u到v有一条权重为w的有向边
        // w == 0时 无法从u点走到v点
//并在读边时,用g[u][v]=g[v][u]=w; 表示u点与v点有一条无向边

code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e4;
int g[maxn][maxn]; // g[u][v]==w 表示u到v有一条权重为w有向边
int n,m;
void AdjacencyMatrix(){
    int u,v,w;//u,v表示点的编号 w表示边的权重
    memset(g,0,sizeof(g));//初始化 无边
    for(int i = 0;i < m;i++){ // 读入边
        scanf("%d %d %d",&u,&v,&w);
        g[u][v] = g[v][u] = w;//因为是无向图 所以用俩个有向边表示无向边
    }
}
int main()
{
    cin>>n>>m;
    AdjacencyMatrix();
    return 0;
}

有向的处理方式

就是在读边时,正常得读边就行

g[u][v] // g[u][v]==1时 表示u到v有一条有向边
        // g[u][v]==0时 无法从u点走到v点
//并在读边时,用g[u][v] =1; 表示u点与v点有一条无向边

code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e4;
int g[maxn][maxn]; // g[u][v]==w 表示u到v有一条权重为w有向边
int n,m;
void AdjacencyMatrix(){
    int u,v,w;//u,v表示点的编号 w表示边的权重
    memset(g,0,sizeof(g));//初始化 无边
    for(int i = 0;i < m;i++){ // 读入边
        scanf("%d %d",&u,&v,&w);
        g[u][v] = 1;//因为是有向图  g[u][v]==1 表示u到v有一条有向边
    }
}
int main()
{
    cin>>n>>m;
    AdjacencyMatrix();
    return 0;
}

邻接表

对于该题,图可以这样画出:
在这里插入图片描述
我们也可以这样来表示每个点的关系
对于
1 : 2、3
2: 1、4、5
3: 1、6、7
4: 2
5: 2
6: 3
7: 3
即:
对于1点 可以到达2点和3点;
对于2点可以到达1点4点5点;

由这些数据,我们发现了:
对于每一个点,都关联着不一定数量的其他点
这不就正像一个vector< int >a
a是某一个点

所以存图时,
可以选择用vector< int >a[maxn];
或者vector<vector< int > >a;

无权无向时

vector<int>g[maxn];//g[u] == v 表示u到v有一条有向边

CODE:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e4;
vector<int>g[maxn];//g[u] == v 表示u到v有一条有向边
int n,m;
void adjacencyList(){
    int u,v,w;//u,v表示点的编号 w表示边的权重
    for(int i = 0;i < m;i++){
        scanf("%d %d",&u,&v);
        g[u].push_back(v);//把v存入编号为u的vector数组,表示u到v有一条有向边。
        g[v].push_back(u);//由于是无向边,用俩条有向边表示无向边
    }

}
int main(int argc, char const *argv[])
{
    cin>>n>>m;
    adjacencyList();
    return 0;
}

有权时的处理

typedef pair<int,int>pii;//pii.first表示v,pii.second表示u到v的w权重
vector<pii>g[maxn];//g[u]表示u 到 g[u].first == v 有一个有向边; g[u].second == w 表示该有向边的权重

CODE:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e4;
typedef pair<int,int>pii;//pii.first表示v,pii.second表示u到v的w权重
vector<pii>g[maxn];//g[u]表示u 到 g[u].first == v 有一个有向边; g[u].second == w 表示该有向边的权重
int n,m;
void adjacencyList(){
    int u,v,w;//u,v表示点的编号 w表示边的权重
    for(int i = 0;i < m;i++){
        scanf("%d %d %d",&u,&v,&w);
        pii now(v,w);//把v存到now结构体的first,w存到now结构体的second。
        g[u].push_back(now);//把now结构体存入编号为u的vector数组
    }

}
int main(int argc, char const *argv[])
{
    cin>>n>>m;
    adjacencyList();
    return 0;
}

有向时的处理

vector<int>g[maxn];//g[u] == v 表示u到v有一条有向边

CODE:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e4;
vector<int>g[maxn];//g[u] == v 表示u到v有一条有向边
int n,m;
void adjacencyList(){
    int u,v,w;//u,v表示点的编号 w表示边的权重
    for(int i = 0;i < m;i++){
        scanf("%d %d",&u,&v);
        g[u].push_back(v);//把v存入编号为u的vector数组,表示u到v有一条有向边。
    }

}
int main(int argc, char const *argv[])
{
    cin>>n>>m;
    adjacencyList();
    return 0;
}

前向星

(据某大佬的讲述,前向星能做的邻接表大概都能做)

但这也是一种存图的思路,可以拓宽一下思维方式:

我们知道,邻接表存图,是用一个点当作一个vector不定长数组里面存其他点
那么用前向星,就是用链表 来存边的信息

int nxt[maxn],head[maxn],to[maxn];
nxt[i] 存编号为i的边的下一个点
head[i] 存起点i的第一条边的编号
to[i] 存编号为i的边的终点。

CODE:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e4;
int nxt[maxn],head[maxn],to[maxn];//nxt[i] 存编号为i的边的下一个点、head[i] 存起点i的第一条边的编号、to[i] 存编号为i的边的终点。
int n,m;
int cnt = -1;//表示边的编号 就一直++得去;
void add(int u,int v){
    nxt[++cnt] = head[u];//把u的第一条边存入 当前边 0 的后继(在nxt中先存的是-1 然后是对应边的编号) 
    head[u] = cnt;//把编号为 0 的边存入 u的第一条边
    to[cnt] = v;//表示编号为cnt的边的后继点是v;
}
void read(int u){
    for(int i = head[u];i!=-1;i = nxt[i]){//链表遍历
        int v = to[i];//to[i]存的就是那条边的后继点
    }
}

int main(int argc, char const *argv[])
{
    memset(head,-1,sizeof(head));//做记号,表示-1时 无边或边被全部读完
    cin>>n>>m;
    int u,v;//u,v表示点的编号 w表示边的权重
    for(int i = 0;i < m;i++){
        scanf("%d %d",&u,&v);
    }
    return 0;
}

总结

(邻接矩阵只适用于没有重边(或重边可以忽略)的情况,一般情况下不用考虑)

  • 邻接矩阵

空间复杂度高:为O(n^2)
查询一条边的存在情况快: O(1)
遍历一个点的所有出边 :O(n)
遍历整个图 :O(n^2)

  • 邻接表
    邻接表基本可以应对所有情况(除了要快速查询一条边要用邻接矩阵)

空间复杂度低 :为O(m) m为边数
查询某条边 : O(d+(u))即这个点的边数 ,如果边有排序,则通过二分就是O(log d+(u)) log边数的事件。
遍历一个点的所有出边 :O(d+(u))即该点的边数
遍历整个图 :O(n+m) 事件为点的个数加边数。

  • 前向星
    与邻接表基本一致。
    优点是边有编号(在更厉害点的算法中有用)

求最短路推荐用邻接表存图(足够应付大部分情况)。

以上就是这篇文章的全部内容。如果看到这里不如点赞评论支持一下啦!

下一篇文章,就是重头戏——常见的最短路算法
预告
拓扑排序、SPFA、Dijkstra、Bellman-ford、floy(多源最短路中的情况
*)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值