当前位置: 首页 > news >正文

算法基础——树

一、树的概念

1. 树的定义

       树型结构是⼀类重要的⾮线性数据结构。

2. 树的基本术语 

3. 有序树和⽆序树

4. 有根树和⽆根树

注意

        如果是⽆根树,⽗⼦关系不明确,此时我们需要把所有的情况都存下来。⽐如a和b之间有⼀条 边,我们不仅要存a有⼀个孩⼦b,也要存b有⼀个孩⼦a。

        甚⾄有的时候,在有根树的题⽬⾥,也要这样存储。

二、树的存储

1. 孩⼦表⽰法

2. 实现⽅式⼀:⽤vector数组实现

#include <iostream>
#include <vector>

using namespace std;

const int N = 1e5 + 10;

int n;
vector<int> edges[N]; // 存储树

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b; cin >> a >> b;
        // a 和 b 之间有一条边
        edges[a].push_back(b);
        edges[b].push_back(a);
    }


    return 0;
}

3. 实现⽅式⼆:链式前向星

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

// 链式前向星
int h[N], e[N * 2], ne[N * 2], id;
int n;

// 其实就是把 b 头插到 a 所在的链表后面
void add(int a, int b)
{
    id++;
    e[id] = b;
    ne[id] = h[a];
    h[a] = id;
}

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b; cin >> a >> b;
        // a 和 b 之间有一条边
        add(a, b); add(b, a);
    }


    return 0;
}

关于vector数组以及链式前向星:

  • 前者由于⽤到了容器vector,实际运⾏起来相⽐较于后者更耗时,因为vector是动态实现的。
  • 但是在如今的算法竞赛中,⼀般不会⽆聊到卡这种常数时间。也就是vector虽然慢,但不会因此⽽超时。

三、树的遍历 

1. 深度优先遍历-DFS

间复杂度:

简单估计⼀下,在 dfs   整个过程,会把树所有的边扫描量两遍。边的数量为n 1 ,因此间复度为O(N)

空间复杂度:

最差况下,结点个数为  的树,⾼度也是 n 也就是变成⼀个链表。此递归的深度也是  n 此时的空间度为O(N)

 

2. 宽度优先遍历-BFS

间复杂度:

所有结点只会次,然后出队次,因此度为 O(N)

空间复杂度:

最坏情况下,所有的根结点都在同⼀层,此队列⾥⾯多有 n 个元素,空间杂度为 O(N)

 3. 练习

(1)题目描述 

题⽬描述:

定⼀棵树,该树⼀共 个结点,编号分别是 1 ∼ n

描述:

⼀⾏⼀个整数 n 表⽰ n 结点。

来 n ⾏,每⾏两个整数 u, v 表⽰ u 之间有条边。

测试例:

 11
 1 3
 7 3
 3 10
 1 5
 4 5
 2 1
 11 2
 6 11
 11 8
 11 9

(2)实现代码

深度优先遍历——DFS

// // 用 vector 数组存储树

// #include <iostream>
// #include <vector>

// using namespace std;

// const int N = 1e5 + 10;

// int n;
// vector<int> edges[N]; // 存储图
// bool st[N]; // 标记哪些点已经访问过了

void dfs(int u)
{
    cout << u << " ";
    st[u] = true; // 当前这个点已经访问过了

    // 访问所有的孩子
    for(auto v : edges[u])
    {
        if(!st[v])
        {
            dfs(v);
        }
    }
}

// int main()
// {
//     // 建树
//     cin >> n;
//     for(int i = 1; i < n; i++)
//     {
//         int a, b; cin >> a >> b;
//         edges[a].push_back(b);
//         edges[b].push_back(a);
//     }

//     // 深度优先遍历
//     dfs(1);

//     return 0;
// }

// 用链式前向星存储
#include <iostream>

using namespace std;

const int N = 1e5 + 10;

// 存图
int h[N], e[N * 2], ne[N * 2], id;
int n;

bool st[N]; // 标记哪些点已经访问过了

void add(int a, int b)
{
    id++;
    e[id] = b;

    ne[id] = h[a];
    h[a] = id;
}

void dfs(int u)
{
    cout << u << " ";
    st[u] = true;

    for(int i = h[u]; i; i = ne[i])
    {
        int v = e[i]; // 孩子
        if(!st[v])
        {
            dfs(v);
        }
    }
}

int main()
{
    // 建树
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b; cin >> a >> b;
        add(a, b); add(b, a);
    }

    // 深度优先遍历
    dfs(1);

    return 0;
}


// 斐波那契数列
int fib(int n)
{
    if(n == 0 || n == 1) return n;

    return fib(n - 1) + fib(n - 2);
}

 宽度优先遍历——BFS

// 用 vector 数组存树
// #include <iostream>
// #include <queue>
// #include <vector>

// using namespace std;

// const int N = 1e5 + 10;

// int n;
// vector<int> edges[N]; // 存树
// bool st[N]; // 标记哪些节点已经访问过了

// void bfs()
// {
//     queue<int> q;
//     q.push(1);
//     st[1] = true;

//     while(q.size())
//     {
//         int u = q.front(); q.pop();
//         cout << u << " ";

//         for(auto v : edges[u])
//         {
//             if(!st[v])
//             {
//                 q.push(v);
//                 st[v] = true;
//             }
//         }
//     }
// }

// int main()
// {
//     // 建树
//     cin >> n;
//     for(int i = 1; i < n; i++)
//     {
//         int a, b; cin >> a >> b;
//         edges[a].push_back(b);
//         edges[b].push_back(a);
//     }

//     bfs();

//     return 0;
// }


// 用链式前向星存储树
#include <iostream>
#include <queue>

using namespace std;

const int N = 1e5 + 10;

int n;
int h[N], e[N * 2], ne[N * 2], id;
bool st[N];

void add(int a, int b)
{
    id++;
    e[id] = b;
    ne[id] = h[a];
    h[a] = id;
}

void bfs()
{
    queue<int> q;
    q.push(1);
    st[1] = true;

    while(q.size())
    {
        int u = q.front(); q.pop();
        cout << u << " ";

        for(int i = h[u]; i; i = ne[i])
        {
            int v = e[i];
            if(!st[v])
            {
                q.push(v);
                st[v] = true;
            }
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b; cin >> a >> b;
        add(a, b); add(b, a);
    }

    bfs();

    return 0;
}

相关文章:

  • RAG基建之PDF解析的“流水线”魔法之旅
  • 网络安全-网络安全基础
  • freecad gear模块 生成齿轮导出fcstd step
  • 20组电影美学RED摄像摄影机视频胶片模拟色彩分级调色LUT预设包 Pixflow – CL – RED Camera LUTs
  • 项目实战--权限列表
  • Mybatis日志模块分析--适配器模式+代理模式
  • 身份验证:区块链如何让用户掌控一切
  • Scrapy对比Selenium:哪个最适合您的网络爬虫项目
  • 深度学习Note.5(机器学习2)
  • Unity中UDP异步通信常用API使用
  • Python小练习系列 Vol.7:全排列生成(回溯算法模板题)
  • Spring笔记03-依赖注入
  • Javaweb后端登录认证 登录校验 过滤器 filter令牌校验,执行流程,拦截路径
  • 业之峰与宏图智能战略携手,开启家装数字化新篇章
  • excel 时间戳 转日期
  • shop搜索需求及测试点
  • 01-Docker 安装
  • 知识图谱之知识抽取:从数据海洋中 “捞金”
  • Plastiform复制胶泥:高精度表面复制与测量的高效工具
  • 如何学习Python编程?
  • 天津个人做网站/赵阳竞价培训
  • 网站建设 实训题/赣州seo公司
  • 水泵行业网站怎么做/网站收录查询爱站
  • 做外贸女装有哪些网站有哪些/seo优化排名技术百度教程
  • 珠宝网站建商台北/广州seo和网络推广
  • 常州公诚建设项目管理有限公司官方网站/珠海网站seo