从零备战蓝桥杯——图(基础理论,图的基础创建,基础深度宽度优先遍历)

请添加图片描述


双非刷leetcode,2023年蓝桥杯,qwq加油吧,无论结果如何总会有收获!一起加油,我是跟着英雄哥的那个思维导图刷leetcode的,大家也可以看看所有涉及到的题目用leetcode搜索就可以哦,因为避让添加外链,一起加油!!!

本篇是基础篇!非leetcode刷题,有图的基础的可以跳过下一篇会写一些与图相关的进阶 算法

图的创建与初始化

其实大多数的图的算法题以及leetcode的题基本上都是用邻接矩阵来或者使用vector容器的方式来储存,所以下面的所有的题我都是用的这两种形式来储存的,我觉得把用链表存的话,太麻烦了!!!这里我们就说两种哈,还有好多,以后补充补充,现在这两种就够用了哈。

vector容器储存

告诉一下小白这个vector容器就相当于一个数组,要使用头文件#include<vector>

其实这个vector容器就是我们所说的临界链表的一种vector(数组)的表现用vector有几个好处一个是vector中的类型可以定义为类如vector<T> a[N]来表现储存这就好说了,比如一个节点有好几个权,比如你找男朋友有{int 身高;int 存款; bool 是不是舔狗;}{int 身高;int 存款; bool 是不是舔狗;}等权重,这样就是我们可以定义一个T类来储存这些权重,解决了权重的储存问题,十分的好用,十分十分十分十分十分的好用,

而我们初始化的时候就是在一个节点的后面来如果要添加temp——b(权)我们就可以把b节点和他的权值都做成一个T类然后使用a[temp].push_back(T)进行添加.

无权的情况:

    for (int i = 0; i < e; i++)
    {
        int a, a1;
        cin >> a >> a1;
        map[a].push_back(a1);
    }

有权的情况:

class T
{
public:
    int x;
    int dis;
    T(int x, int dis)
    {
        this->x = x;
        this->dis = dis;
    }
};

    for (int i = 0; i < e; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        T temp(b, c);
        graph[a].push_back(temp);
    }

邻接矩阵的储存

咋储存,相信学过离散数学的各位一定都知道。

比如说:如果temp——b(权)的我们在储存的时候就要在一个二维数组中a[N] [N]进行储存

如果是有向图我们就可以a[temp] [b]=权;

如果是无向图我们就可以a[b] [temp]=权;

如果没有权就可以a[b] [temp]=1;来表示他俩有关系!;

    for (int i = 0; i < n; i++)
    {
        cin >> temp;
        a[temp[0] - 'A'][temp[1] - 'A'] = 1;
        a[temp[1] - 'A'][temp[0] - 'A'] = 1;
    }

啥也不说了,直接上题

基础创建题;

这个题还是十分简单的,只要知道邻接矩阵是什么意思一般就能自己打出来了,快来试试吧

邻接矩阵表示法创建无向图

采用邻接矩阵表示法创建无向图G ,依次输出各顶点的度。

输入格式:

输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶点数和边数。
输入第二行为顶点的信息,每个顶点只能用一个字符表示。
依次输入j行,每行输入一条边依附的顶点。

输出格式:

依次输出各顶点的度,行末没有最后的空格。

输入样例:

5 7
ABCDE
AB
AD
BC
BE
CD
CE
DE

输出样例:

2 3 3 3 3

我觉得吧,这种题就是搞个二维数组就完事了你知道这个邻接矩阵是什么就好办了~


#include <iostream>
#include <string.h>
using namespace std;
int main()
{
    int n, m;
    cin >> m >> n;
    string s, temp;
    int a[m][m];
    cin >> s;
    memset(a, 0, sizeof(a));
    for (int i = 0; i < n; i++)
    {
        cin >> temp;
        a[temp[0] - 'A'][temp[1] - 'A'] = 1;
        a[temp[1] - 'A'][temp[0] - 'A'] = 1;
    }
    int flag = 0;
    for (int i = 0; i < m; i++)
    {
        int count = 0;
        for (int j = 0; j < m; j++)
        {
            if (a[i][j] == 1)
            {
                count++;
            }
        }
        if (flag == 0)
        {
            cout << count;
            flag = 1;
        }
        else
        {
            cout << " " << count;
        }
    }
    system("pause");
}

图的深度优先遍历;


如果你能看到我的图,就说明你已经学完二叉树了对不对!!!别高数我你还不知道啥叫树!!你要是不知道什么叫树的深度遍历的话我们就要直接跳转到我的上一篇文章:二叉树及相关题目(基础篇)看看树是怎么遍历的。

树的深度遍历就是利用递归,传入根节点,对本节点来进行打印啊什么的操作继续调用函数来访问左孩子,来访问右孩子,对不对

那么图的遍历和树的遍历一样都是可以用递推来访问

可是这个时候我们就遇到了一个问题,要是这个图是连通图是不是就不想树那样访问到左右孩子都是空就不用管了。那就会一直一直一直进行下去,怎么防止这样的情况发生呢?我们就可以直接使用一个visit[N]来进行标记,如果这个节点已经访问过了就可以标记一下!说!visit[****]=1;来表示已经访问过了;

这样我们就可以进行深度优先遍历了,下面用vector创建图的方法来进行说明一下

如果a[一个节点] [相邻节点1,相邻节点2,相邻节点3,相邻节点4,相邻节点5]

我们在函数的时候传入一个节点a之后如何访问与a相连的那些节点呢,其实是比较简单的,因为在添加过程中我们就是把这个节点相连的节点都放到这个节点的后面了。如相邻的第一个节点就是a[data][0]第二个节点就是a[data][1].所以你懂的可以用for循环+递归的方式来每个节点,这里和树基本上就是一样的这几个相邻节点就是12345的儿子来进行访问。

而进行访问的过程中要注意的是要标记visit[data]=1;嗯嗯嗯。就是这样搞了。

void dfs(int data)
{
    if (data == n)
    {
        return;
    }
    visit[data] = 1;
    cout << data << " ";
    for (int i = 0; i < map[data].size(); i++)
    {
        if (!visit[map[data][i]])
            dfs(map[data][i]);
    }
}

主函数中要把所有肯能的节点都访问一遍,因为图不一定都是相连的,还可能有多个开始节点:

for (int i = 0; i < n; i++)
    {
        if (!visit[i])
        {
            dfs(i);
        }
    }

来做个题试试吧!


有向图深度优先遍历

编写程序对给定的有向图(不一定连通)进行深度优先遍历,图中包含n个顶点,编号为0至n-1。本题限定在深度优先遍历过程中,如果同时出现多个待访问的顶点,则优先选择编号最小的一个进行访问,以顶点0为遍历起点。

输入格式:

输入第一行为两个整数n和e,分别表示图的顶点数和边数,其中n不超过20000,e不超过50。接下来e行表示每条边的信息,每行为两个整数a、b,表示该边的端点编号,但各边并非按端点编号顺序排列。

输出格式:

输出为一行整数,每个整数后一个空格,即该有向图的深度优先遍历结点序列。

输入样例1:

3 3
0 1
1 2
0 2

输出样例1:

0 1 2 

输入样例2:

4 4
0 2
0 1
1 2
3 0

输出样例2:

0 1 2 3 
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<int> map[20001];
vector<int> visit(20001, 0);
int n, e;

void dfs(int data)
{
    if (data == n)
    {
        return;
    }
    visit[data] = 1;
    cout << data << " ";
    for (int i = 0; i < map[data].size(); i++)
    {
        if (!visit[map[data][i]])
            dfs(map[data][i]);
    }
}

int main()
{

    cin >> n >> e;

    for (int i = 0; i < e; i++)
    {
        int a, a1;
        cin >> a >> a1;
        map[a].push_back(a1);
    }

    for (int i = 0; i < n; i++)
    {
        if (!map[i].empty())
        {
            sort(map[i].begin(), map[i].end());
        }
    }

    for (int i = 0; i < n; i++)
    {
        if (!visit[i])
        {
            dfs(i);
        }
    }
    system("pause");
}

宽度优先遍历

深度优先遍历会了,宽度优先遍历就会了,别问为什么,问就是因为!!!!因为你学过树,就是树的遍历来多个visit数组而这个也是一样的,就多了个visit数组,其他的都一样,自己写写吧!顺便写一下深度优先遍历

7-5 列出连通集

给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:

输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:

按照"{ v1 v2 … v**k }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

输入样例:

8 6
0 7
0 1
2 0
4 1
2 4
3 5

输出样例:

{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }
#include <iostream>
#include <vector>
#include <limits.h>
#include <string.h>
#include <algorithm>
#include <queue>

using namespace std;

int n, e;

vector<int> graph[15];
vector<int> visit1(15, 0);
vector<int> visit2(15, 0);

void dfs(int u)//深度优先遍历
{
    if (u == n)
    {
        return;
    }

    cout << u << " ";
    visit1[u] = 1;
    for (int i = 0; i < graph[u].size(); i++)
    {
        if (!visit1[graph[u][i]])
        {
            dfs(graph[u][i]);
        }
    }
}

void bfs(int t)//宽度优先遍历
{
   queue<int>q;
   q.push(t);
   visit2[t]=1;
   while (!q.empty())
   {
        int t=q.front();
        cout<<t<<" ";
        q.pop();
        for (int i = 0; i < graph[t].size(); i++)
        {
            if(!visit2[graph[t][i]])
            {
                q.push(graph[t][i]);
                visit2[graph[t][i]]=1;
            }

        }
        
   }
   
}
int main()
{

    cin >> n >> e;
    for (int i = 0; i < e; i++)
    {
        int a, b;
        cin >> a >> b;
        graph[a].push_back(b);
        graph[b].push_back(a);
    }

    for (int j = 0; j < n; j++)
    {
        sort(graph[j].begin(), graph[j].end());
    }

    for (int i = 0; i < n; i++)
    {
        if (!visit1[i])
        {
            cout << "{ ";
            dfs(i);
            cout << "}" << endl;
        }
    }

    for (int i = 0; i < n; i++)
    {
        if (!visit2[i])
        {
            cout << "{ ";
            bfs(i);
            cout << "}" << endl;
        }
    }
    system("pause");
}

Love is worth years.❤
热爱可抵岁月漫长。

​​​​
本文部分思路来源于网络如有侵权联系删除~

在这里插入图片描述

Love is worth years.❤
热爱可抵岁月漫长。

​​​​
本文部分思路来源于网络如有侵权联系删除~

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

c0re

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

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

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

打赏作者

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

抵扣说明:

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

余额充值