Acwing-基础算法课笔记之搜索与图论

一、BFS

1、根据字符串中得位置,求在矩阵中的位置

给出一个长度为9的字符串一维数组,计算出x字符在 3 × 3 3\times3 3×3矩阵二维数组当中的坐标
例如:字符串1234x5678
设t为x在字符串的所在位置,即 t = 4 t=4 t=4,利用 x = t / 3 x=t / 3 x=t/3 y = t % 3 y=t\% 3 y=t%3
求得 x = 1 , y = 1 x=1,y=1 x=1y=1,即:

1 2 3 4 x 5 6 7 8 \def\arraystretch{1.5} \begin{array}{c:c:c} 1 & 2 & 3 \\ \hline 4 & x & 5 \\ \hdashline 6 & 7 & 8 \end{array} 1462x7358

2、运用到的工具-d.count()

以下是关于 unordered_map 和 unordered_set 中的 count 成员函数的简要说明:

( 1 ) u n o r d e r e d _ m a p (1)unordered\_map 1unordered_map

#include <unordered_map>
#include <iostream>
using namespace std;
int main() {
    std::unordered_map<int, int> myMap;
    myMap[1] = 10;
    myMap[2] = 20;
    myMap[3] = 30;
    int keyToFind = 2;
    // 使用 count 函数检查特定键是否存在于 map 中
    if (myMap.count(keyToFind) > 0) {
        std::cout << "Key " << keyToFind << " exists in the map." << std::endl;
    } else {
        std::cout << "Key " << keyToFind << " does not exist in the map." << std::endl;
    }
    return 0;
}

在上面的例子中,myMap.count(keyToFind) 返回键 keyToFind 在 unordered_map 中出现的次数,因为在 unordered_map 中每个键都是唯一的,所以该函数实际上只能返回 1 或 0。

( 2 ) u n o r d e r e d _ s e t (2)unordered\_set 2unordered_set

#include <unordered_set>
#include <iostream>
using namespace std;
int main() {
    std::unordered_set<int> mySet = {1, 2, 3, 4, 5};
    int elementToFind = 3;
    // 使用 count 函数检查特定元素是否存在于 set 中
    if (mySet.count(elementToFind) > 0) {
        std::cout << "Element " << elementToFind << " exists in the set." << std::endl;
    } else {
        std::cout << "Element " << elementToFind << " does not exist in the set." << std::endl;
    }
    return 0;
}

在上面的例子中,mySet.count(elementToFind) 返回元素 elementToFind 在 unordered_set 中出现的次数,因为在 unordered_set 中每个元素都是唯一的,所以该函数实际上只能返回 1 或 0。

注意:在C++中,d.count(t) 的语法并不是标准 C++ 的一部分。可能是出现在个特定的上下文或特定的类(例如自定义的类)中。然而,如果你是在讨论标准库中的 unordered_map 或 unordered_set 的成员函数count,那么这个函数用于检查容器中特定元素的数量。

3、运用到的工具-t.find()

在C++中,t.find(‘x’)通常用于在字符串 t 中查找字符 ‘x’ 的位置。这是string类的成员函数,它返回字符 ‘x’ 在字符串中第一次出现的位置(索引)。

#include <iostream>
#include <string>
using namespace std;
int main() {
    std::string t = "Hello, world!";
    size_t position = t.find('x');
    if (position != std::string::npos) {
        std::cout << "Found 'x' at position: " << position << std::endl;
    } else {
        std::cout << "'x' not found in the string." << std::endl;
    }
    return 0;
}

在这个例子中,如果字符串 “Hello, world!” 中包含字符 ‘x’,则输出找到 ‘x’ 的位置;否则,输出 “‘x’ not found in the string.”。注意:t.find()不是STL当中的工具

4、从二维数组的位置变成一维数组的位置

例如:
二维数组:
1 2 3 4 x 5 6 7 8 \def\arraystretch{1.5} \begin{array}{c:c:c} 1 & 2 & 3 \\ \hline 4 & x & 5 \\ \hdashline 6 & 7 & 8 \end{array} 1462x7358
当中字符’x’的坐标为 x = 1 , y = 1 x=1,y=1 x=1,y=1

利用公式: t = x × 3 + y t=x\times3+y t=x×3+y得到字符’x’在一维数组中的位置
如下:

1234 x 5678 1234x5678 1234x5678
当中字符’x’的坐标为 t = 4 t=4 t=4

二、树与图的深度优先遍历

1、树的重心

如图所示:
在这里插入图片描述
∙ \bullet 任取一点u,若以u为重心,则分为两类:
一类是u的子树,一类是u上面的部分。
∙ \bullet 需要算出u的最大子树的节点数和u上面的部分的节点数,然后取二者的最大值即可。

⋆ \star size:记录u的最大子树的节点数;
⋆ \star sum:记录以u为根的子树的节点数;
⋆ \star n-sum:u上面的部分的节点数;
⋆ \star ans=max(size,n-sum);
注意: n 为节点的个数 注意:n为节点的个数 注意:n为节点的个数

2、邻接表模板

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;
// 添加一条边a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
// 初始化
idx = 0;
memset(h, -1, sizeof h);

Acwing-邻接表模板

3、深度优先遍历模板

int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

Acwing-深度优先遍历模板

三、树与图的广度优先遍历

1、宽度优先遍历模板

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);
while (q.size())
{
    int t = q.front();
    q.pop();
    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

Acwing-宽度优先遍历模板

四、拓扑排序

1、拓扑排序的概念

给定一张有向无环图,排出所有顶点的一个序列 A A A满足:
对于图中的每一条有向边 ( x , y ) (x,y) (x,y) x x x A A A中都出现在 y y y之前,则称 A A A,是该图的顶点的一个拓扑序。
如图, { 2 , 3 , 5 , 1 , 7 , 4 , 6 } \{ 2,3,5,1,7,4,6\} {2,3,5,1,7,4,6} { 3 , 2 , 1 , 5 , 7 , 6 , 4 } \{ 3,2,1,5,7,6,4\} {3,2,1,5,7,6,4}都是合法的拓扑序。
在这里插入图片描述
拓扑排序可以判断有向图中是否有环,可以生成拓扑序列。
B站董晓算法-拓扑排序的详细讲解
注意: 拓扑序列一定是针对有向无环图而言的,并且一个有向无环图的拓扑序不是唯一的。 注意:\textcolor{red}{拓扑序列一定是针对有向无环图而言的,并且一个有向无环图的拓扑序不是唯一的。} 注意:拓扑序列一定是针对有向无环图而言的,并且一个有向无环图的拓扑序不是唯一的。
在这里插入图片描述

2、拓扑排序模板

时间复杂度 O ( n + m ) O(n+m) O(n+m) n n n表示点数, m m m表示边数

bool topsort()
{
    int hh = 0, tt = -1;
    // d[i] 存储点i的入度
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[ ++ tt] = j;
        }
    }
    // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
    return tt == n - 1;
}

Acwing-拓扑排序模板

五、Dijkstra

1、朴素版Dijkstra算法

如下图所示:
在这里插入图片描述
∙ \bullet 一开始距离出发点的距离都设为 ∞ \infty

节点距离出发点前面点
1 ∞ \infty
2 ∞ \infty
3 ∞ \infty

⇓ \Downarrow
∙ \bullet 一开始节点 1 的距离出发点距离是自己,所以距离出发点距离为 0,标记节点 1 已被经过

节点距离出发点前面点
1 ✓ \checkmark 0
2 ∞ \infty
3 ∞ \infty

⇓ \Downarrow
∙ \bullet 然后节点 1 接下来有两个路径分别是节点 2 和节点 3,填写距离两个节点的路径权值,并填写前面节点的记号,因为距离节点 2 最近,所以选择节点 2 的路径,并标记节点 2 已被经过

节点距离出发点前面点
1 ✓ \checkmark 0
2 ✓ \checkmark 21
341

⇓ \Downarrow
∙ \bullet 因为接下来是走节点 2 的路径,所以距离节点 3 的距离为 1

节点距离出发点前面点
1 ✓ \checkmark 0
2 ✓ \checkmark 21
312

⇓ \Downarrow
∙ \bullet 接下来是走从节点 1 → \rarr 节点 3 的路径,由于节点 3 距离节点 1 的距离 4,明显大于路径节点 1 → \rarr 节点 2 → \rarr 节点 3 的距离,所以不用在更新,直接标记节点 3 已被经过

节点距离出发点前面点
1 ✓ \checkmark 0
2 ✓ \checkmark 21
3 ✓ \checkmark 12

∙ \bullet 所以最终得最短距离为 3

注意: 对于边数很多的稠密图用邻接矩阵来存,变数不多的稀疏图用邻接表来存。 注意:\textcolor{red}{对于边数很多的稠密图用邻接矩阵来存,变数不多的稀疏图用邻接表来存。} 注意:对于边数很多的稠密图用邻接矩阵来存,变数不多的稀疏图用邻接表来存。

2、朴素Dijkstra算法模板

时间复杂度 O ( n 2 + m ) O(\gdef\foo#1{#1^2} \foo{n} + {m}) O(n2+m) n n n表示点数, m m m表示边数

int g[N][N];  // 存储每条边
int dist[N];  // 存储1号点到每个点的最短距离
bool st[N];   // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        // 用t更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);
        st[t] = true;
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

Acwing-朴素Dijkstra算法模板

2、堆优化版dijkstra算法

(1)优先队列的使用-priority_queue(也称堆)

∙ \bullet 用数组来模拟的一棵完全二叉树
∙ \bullet 操作:第一种是入队,第二种是出队
∙ \bullet 这是B站某博主对堆的详细讲解视频
∙ \bullet priority_queue的基本使用代码如下:

// 升序队列,小顶堆,大于当前节点要下沉
priority_queue <int, vector<int>, greater<int>> q;// top最小
// 降序队列,大顶堆,小于当前节点要下沉
priority_queue <int, vector<int>, less<int>> q;// top最大
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
// 默认是less,就是降序队列
priority_queue <int> q;

(2)堆优化版dijkstra算法模板

时间复杂度 O ( m l o g n ) O(mlogn) O(mlogn) n n n表示点数, m m m表示边数

typedef pair<int, int> PII;
int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定
// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

Acwing-堆优化版dijkstra算法模板

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会敲代码的狗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值