c++算法学习笔记 (8) 树与图部分

简介: c++算法学习笔记 (8) 树与图部分

1.树与图的存储

(1)邻接矩阵

(2)邻接表

// 链式前向星模板(数组模拟)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx; // 头,边,next值;n个单链表,所以有n个头h[N]
void add(int a, int b)
{ // 在头为a的表中头插b(此时编号为idx)
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}
int main()
{
    memset(h, -1, sizeof h);
}


2.树与图的遍历

(1)深度优先遍历DFS

// 数和图的DFS模板
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx;
bool st[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}
void dfs(int u)
{
    st[u] = true; // 标记已被搜过
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
            dfs(j);
    }
}
int main()
{
    memset(h, -1, sizeof h);
    dfs(1);
}


树的重心

给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。


输入格式

第一行包含整数 n,表示树的结点数。

接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。

输出格式

输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。

数据范围

1≤n≤105

输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6


输出样例:
4


思路:删掉某一个点,此点的孩子各成一个连通块(有几个孩子就有几个连通块),整棵树除去此点及其孩子成为一个连通块

4df359e6c38e40a8a7e7a9b3202b8eab.jpg

// 树的重心
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx;
bool st[N];
int ans = N;
int n;
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}
// 返回以u为根的子树里点的数量
int dfs(int u)
{
    st[u] = true;         // 标记已被搜过
    int sum = 1, res = 0; // sum:当前子树大小(此时为要删的节点,所以为1);res:删掉点后,连通块的最大值
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i]; // e[]:一条边链接i和j(=e[i])
        if (!st[j])
        {
            int s = dfs(j); // 子树里点的数量
            res = max(res, s);
            sum += s; // 当前子树大小(=自己+子树)
        }
    }
    res = max(res, n - sum); // 树中除了节点及其子树以外的点,它们构成一个连通块
    ans = min(ans, res);
    return sum;
}
int main()
{
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a); // 无向图
    }
    dfs(1);
    cout << ans << endl;
    return 0;
}

(2)广度优先遍历DFS

图中点的层次

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。

所有边的长度都是 1,点的编号为 1∼n。

请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 a 和 b,表示存在一条从 a 走到 b 的长度为 1 的边。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

数据范围

1≤n,m≤105

输入样例:
4 5
1 2
2 3
3 4
1 3
1 4


输出样例:
1


// 图中点的层次
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx;
int d[N], q[N];
int n, m;
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
int bfs()
{
    queue<int> q;
    q.push(1);
    memset(d, -1, sizeof d); // 初始化距离d
    d[1] = 0;
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (d[j] == -1)
            {
                d[j] = d[t] + 1;
                q.push(j);
            }
        }
    }
    return d[n];
}
int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
 
    cout << bfs() << endl;
    return 0;
}

3.BFS的应用:拓扑排序

有向图的拓扑序列

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1−1。

若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。

输出格式

共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。

否则输出 −1。

数据范围

1≤n,m≤105

输入样例:
3 3
1 2
2 3
1 3


输出样例:
1 2 3


// 拓扑序列
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[N], idx;
int in[N]; // 入度
int n, m;
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
int ans[N];
bool toposort()
{
    int id = 0;
    queue<int> q;
    for (int i = 1; i <= n; i++) // 注意是1~n而不是0~n-1
    {
        if (in[i] == 0)
        {
            q.push(i);
        }
    }
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        id++;
        ans[id] = t; // 记录拓扑序
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            in[j]--;
            if (in[j] == 0)
            {
                q.push(j);
            }
            // 之后不要in[j]++;
        }
    }
    if (id < n)
        return false;
    else
        return true;
}
int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i++)
    {
        int x, y;
        cin >> x >> y;
        add(x, y);
        in[y]++;
    }
    if (toposort())
    {
        for (int i = 1; i <= n; i++)
        {
            cout << ans[i] << " ";
        }
    }
    else
    {
        cout << -1;
    }
    return 0;
}


相关文章
|
28天前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
46 1
|
28天前
|
C++
c++学习笔记07 结构体
C++结构体的详细学习笔记07,涵盖了结构体的定义、使用、数组、指针、嵌套、与函数的交互以及在结构体中使用const的示例和解释。
31 0
|
14天前
|
安全 C语言 C++
C++学习笔记
C++学习笔记
|
16天前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
16 2
|
28天前
|
C++
c++学习笔记02 运算符
C++学习笔记,介绍了C++中的运算符,包括基本的加减乘除、求模、前后置递增递减、赋值运算符、比较运算符和逻辑运算符的使用及其注意事项。
30 6
|
28天前
|
C++
c++学习笔记01 基本知识与数据类型
C++学习笔记,涵盖了C++中的常量定义、数据类型、变量内存大小计算、基本数据类型(整型、实型、字符型、字符串型、布尔型)以及转义字符的使用。
39 4
|
28天前
|
算法 C++
c++学习笔记04 数组
这篇文章是C++学习笔记4,主题是数组。
35 4
|
27天前
|
C++
【学习笔记】【C/C++】 c++字面值常量
【学习笔记】【C/C++】 c++字面值常量
26 1
|
28天前
|
存储 C++
c++学习笔记05 函数
C++函数使用的详细学习笔记05,包括函数的基本格式、值传递、函数声明、以及如何在不同文件中组织函数代码的示例和技巧。
27 0
c++学习笔记05 函数
|
1月前
|
算法 C++ 容器
C++标准库中copy算法的使用
C++标准库中copy算法的使用
15 1