数据结构 图

树是无环连通图,是一种特殊的图。

分类

图分为有向图[边是有方向的]和无向图[边是无方向的]。

无向图(a—b),建立两条有向图(a—>b,b—>a),无向图是一种特殊的有向图。

存储有向图

邻接矩阵 ——用于存储比较稠密的图【O(n^2)】

开一个二维数组,g[a,b],a—>b;如果有权重,存权重,如果没有权重,就是布尔值;不能存储重边。

邻接表

n个点,每个点都有一个单链表,每个点的单链表存储该点可以到达的点,单链表内部点的顺序不重要。

//插入a—>b的边,在a所对应的单链表中插入b

void add(int a, int b) {

e[idx] = b;

ne[idx] = h[a];

h[a] = idx++;

}

有向图的遍历

宽度优先遍历

一层一层搜索

框架

queue <—— 将起始状态插入队列中,即将1号点插入队列

while (queue 不空){

t 每次取队头元素

拓展 t 所有能到的点 x

if(x 未被遍历){

 queue <——x 将 x 入队

d[x]=d[t]+1

}

}

例题——图中点的层次

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n,m≤10^5

输入样例

4 5

1 2

2 3

3 4

1 3

1 4

输出样例

1

代码

#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int d[N], q[N];
void add(int a, int b) {//插入函数
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
int bfs() {
	int hh = 0, tt = 0;
	q[0] = 1;
	memset(d, -1, sizeof d);//初始化距离,-1代表未被初始化
	d[1] = 0;
	while (hh <= tt) {//判断队列是否为空
		int t = q[hh++];//取队头
		for (int i = h[t];i != -1;i = ne[i]) {//扩展队头
			int j = e[i];
			if (d[j] == -1) {
				d[j] = d[t] + 1;
				q[++tt] = 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;
}

深度优先遍历

找到一个起点,然后从这个 起点开始,一条路走向黑

邻接表的深度优先遍历

主函数:

for(int i=0;i<n;i++) dfs(i); //枚举起点

dfs:

利用图中结点的编号进行搜索,e存图中结点的编号

int h[N], e[M], ne[M], idx;//h存n个链表的链表头,e存每个结点的值是多少,ne存每个结点的next值

bool st[N];

//树和图的深度优先搜索

void dfs(int u) {//u是当前dfs到的点

st[u] = true;//标记一下,已经被搜过了

for (int i = h[u];i != -1;i = ne[i]) {//遍历u的所有出边

int j = e[i];//链表中该点在图中的编号

if (!st[j])//如果j没有被搜过,那么就进行搜索

dfs(j);

}

例题——树的重心

给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边【无向图】。

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

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

输入格式

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

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

输出格式

输出一个整数m,表示重心的所有的子树中最大的子树的结点数目。

数据范围

1≤n≤10^5

输入样例

9

1 2

1 7

1 4

2 8

2 5

4 3

3 9

4 6

输出样例

4

代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010, M = N * 2;
int n;
int h[N], e[M], ne[M], idx;//h存n个链表的链表头,e存每个结点的值是多少,ne存每个结点的next值
bool st[N];
int ans = N;//答案,存最小的最大值
//插入a—>b的边,在a所对应的单链表中插入b
void add(int a, int b) {
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
//树和图的深度优先搜索
int dfs(int u) {//u是当前dfs到的点,返回以u为根的子树中,点的数量【以u为根的子树的大小】
	st[u] = true;//标记一下,已经被搜过了
	int sum = 1, res = 0;//res存删去重心后,每一个连通块大小的最大值;sum存当前子树的大小
	for (int i = h[u];i != -1;i = ne[i]) {//遍历u的所有初边
		int j = e[i];//链表中该点在图中的编号
		if (!st[j]) {//如果j没有被搜过,那么就进行搜索
			int s = dfs(j);//当前子树的大小
			res = max(res, s);//当前子树是一个连通块,所以与之前连通块的最大值进行比较
			sum += s;//以u的子结点为根结点的子树是以u为根结点子树的一部分
		}
	}
	res = max(res, n - sum);
	ans = min(ans, res);
	return sum;
}
int main() {
	cin >> n;
	memset(h, -1, sizeof h);//邻接表初始化,头指向-1
	for (int i = 0;i < n - 1;i++) {
		int a, b;
		cin >> a >> b;
		add(a, b);
		add(b, a);
	}
	dfs(1);//可以从任意点开始搜
	cout << ans << endl;
	return 0;
}

特殊的图——树[无环连通图]

概念

在计算器科学中,树(英语:tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

结点

指树中的一个元素;

结点的度

指结点拥有的子树的个数,二叉树的度不大于2;

树的度

指树中的最大结点度数;

叶子

度为0的结点,也称为终端结点;

高度

叶子节点的高度为1,根节点高度最高;

根在第一层,以此类推;

特点

一个父结点可以有多个子结点,一个子结点只能有一个父结点,多个子结点对应一个父结点。

①每个节点有零个或多个子节点;

②没有父节点的节点称为根节点;

③每一个非根节点有且只有一个父节点;

④除了根节点外,每个子节点可以分为多个不相交的子树;

二叉树

概念

每个节点最多含有两个子树的树称为二叉树。(我们一般在书中试题中见到的树是二叉树,但并不意味着所有的树都是二叉树。)

性质

1:二叉树的第i层上至多有2^(i-1)个结点

2:深度为k的二叉树,至多有2^k-1个结点

满二叉树

除最后一层无任何子节点外,每一层上的所有节点都有两个子节点。也可以这样理解,除叶子节点外的所有节点均有两个子节点。节点数达到最大值,所有叶子节点必须在同一层上。

完全二叉树

若设二叉树的深度为h,除第h层外,其它各层 (1~(h-1)层) 的节点数都达到最大个数(即节点均满),第h层所有的节点都连续集中在最左边(虽然最下面一层节点不满,但是所有节点在左边连续排列,空位都在右边),这就是完全二叉树。

性质
  • 结点 i 的子结点为 2*i 和 2*i+1 (前提是都小于总结点数)
  • 结点 i 的父结点为 i/2
  • 如果n个节点的完全二叉树的节点按照层次并按从左到右的顺序从0开始编号:
    • 序号为0的节点是根
    • 对于 i>0,其父节点的编号为(i-1)/2
    • 若 2*i+1<n,其左子节点的序号为 2*i+1,否则没有左子节点
    • 若 2*i+2<n,其右子节点的序号为 2*i+2,否则没有右子节点

遍历

深度优先遍历

按照一条路径尽可能的向前探索,直至检查完一个叶节点。

先序和中序可以唯一确定一个树【在先序中找父结点,在中序确定位置,得到左子树和右子树】

中序和后序可以唯一确定一个树【在后序中找父结点,在中序确定位置,得到左子树和右子树】

先序和后序不能唯一确定一个树【不能确定左子树和右子树的情况】

这三种遍历方法只是访问结点的时机不同,访问结点的路径都是一样的,时间和空间复杂度皆为O(n)

先序 根->左子树->右子树

父 左->右

先输出父节点,然后从左到右输出子节点

中序 左子树->根->右子树

先输出左节点,然后输出父节点,最后输出右节点

后序 左子树->右子树->根

先输出左节点,然后输出右节点,最后输出根节点

宽度优先搜索

在所有路径上齐头并进,按照路径长度由近到远访问节点,即按照二叉树的层数逐层访问树中的节点。

例题

先序:10 6 5 2 7 14 18 17 19

中序:2 5 6 7 10 14 17 18 19

后序:2 5 7 6 17 19 18 14 10

层次:10 6 14 5 7 18 2 17 19

作用

高效地存储和查找字符串集合的数据结构

适用于字符类型不多的字符串

如何存储字符串集合

有一个根节点,但该节点不存放数据。

存储的时候,应该在每个单词的结尾做标记,表示以该字母结尾的结点有一个单词。防止查找时,误以为该树存储了该字符串的子串。例如,树中存储“abcd”,若没有做标记,在查找“abc”是否在树中时,容易误以为该串存在,做标记后,查找到最后一个字符时,应该检测是否有结束标志,若没有则不存在。

如何查找

从根结点开始,挨个字符在对应层上寻找,若找到就继续向下一层找对应的字符,直到找到最后一个字符,观察字符上是否有结束标记,若有则说明该树上存储了该字符串,若是没有则说明该树上没有存储。若是直接没在该层上找到对应的字符,则说明该树上没有存储。

模板

数据含义

  • 整型 idx 用于给所有的存入树的字符进行编号,idx = 0 既表示根节点,又表示空节点(根节点不存数据)
  • 数组 cnt 用于表示以某个 idx 表示的字符为结尾的字符串的个数
  • 二维数组 son 用于存储每个 idx 对应的所有子节点,在 son 中的元素值为0,表示空节点

son 数组中元素的值代表该节点的子节点所在的位置,即 son 数组的高维度的编号。【son[a][b]=c表示该节点的子节点位于son[c]中】例如,先后存入 abc、abcd、abd、bcd。

为什么 son 不能存储每一层的字母,而是使用这种方式存储?

如果 son 存储每一层的字母,则分不清该字母是哪一个节点下的子节点了。

存储

每次存储字符串都是从根节点开始存储。当存储一个字符时,就应该给该字符编号,并根据编号跳到 son 中编号对应的那一层【将一维置为该编号】,该层存储该编号的所有子节点。当存储到字符串的最后一个字符时,应将该字符的编号作为 cnt 数组的下标,令该元素自身数据自增,表示以该编号所表示的字符为结尾的字符串个数+1,即表示该树上存储了多少个相同的字符串。

//存储
void insert(char str[]) {
    int p = 0;//根节点是0
    for (int i = 0;str[i];i++) {//从根节点开始,从前往后遍历
//因为字符串的最后一个字符是空字符,所以可以使用空字符判断该字符串是否到达末尾
        int u = str[i] - 'a';//将“a~z”映射成“0~25”
        if (!son[p][u]){//如果p这个节点不存在u这个儿子,就将其创造出来
            ++idx;
            son[p][u] = idx;//写成son[p][u]=++idx;是未定义行为,一个序列点对应两个副作用
        }
        p = son[p][u];
    }
    cnt[p]++;//表示以p点结尾的单词数多了一个
}

查找——返回字符串出现的次数

查找和存储思路一样,只是发现没存储该字符就直接返回0,表示没存储该字符串,存储该字符时才跳转。

int query(char str[]){
    int p=0;
    for(int i=0;str[i];i++){
        int u=str[i]-'a';
        if(!son[p][u])
            return 0;//不存在该字符串时直接返回0
        p=son[p][u];
    }
    return cnt[p];
}

输出——遍历输出树中所有的字符串

使用 dfs ,将当前的字符编号作为参数传入,表示要遍历该层26个字符,使用一个 string 存储当前遍历所组成的字符串,若遍历到某个字符存在树中,则将其放在 string 的末尾,并检测是否在以该字符编号为结尾的字符串,如果存在则输出。之后以其子节点层数编号作为新的参数执行 dfs 。当26个字符都没有存储在树中,则说明该路径上的所有的字符串遍历完成,应该进行回退,回退到上一层时,应将 string 的最后一个字符删去。之后遍历该层其他字符。

string temp;
void dfs(int p){
    for(int i=0;i<26;i++){
        if(son[p][i]){
            char tp[2]={'a'+i};
            temp.append(tp);
            if(cnt[son[p][i]]){
                cout<<temp<<endl;
            }
            dfs(son[p][i]);
            temp.pop_back();
        }
    }
}

例题——Trie字符串统计

维护一个字符串集合,支持两种操作:

“I x”向集合中插入一个字符串x;

“Q x”询问一个字符串在集合中出现了多少次。

共有N个操作,输入的字符串总长度不超过 10^5,字符串仅包含小写英文字母。

输入格式

第一行包含整数N,表示操作数。

接下来N行,每行包含一个操作指令,指令为”I x”或”Q x”中的一种。

输出格式

对于每个询问指令”Q x”,都要输出一个整数作为结果,表示x在集合中出现的次数。

每个结果占一行。

数据范围

1≤N≤2∗10^4

输入样例

5

I abc

Q abc

Q ab

I ab

Q ab

输出样例

1

0

1

#include<iostream>

using namespace std;

const int N = 100010;

int son[N][26], cnt[N], idx;//下标是0的点,既是根结点,又是空结点

//cnt 存以当前这个点结尾的单词有多少个

char str[N];

//存储

void insert(char str[]) {

int p = 0;

for (int i = 0;str[i];i++) {

int u = str[i] - 'a';

if (!son[p][u])//如果p这个结点不存在u这个儿子,就将其创造出来

son[p][u] = ++idx;

p = son[p][u];

}

cnt[p]++;

}

//查询

int query(char str[]) {

int p = 0;

for (int i = 0;str[i];i++) {

int u = str[i] - 'a';

if (!son[p][u])

return 0;

p = son[p][u];

}

return cnt[p];

}

int main() {

int n;

scanf("%d", &n);

while (n--) {

char op[2];

scanf("%s%s", op, str);

if (op[0] == 'I')

insert(str);

else

printf("%d\n", query(str));

}

return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/174822.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mysql数据库 17.Mysql存储引擎

Mysql体系结构分为4层&#xff1a; 1.连接层 最上层是一些客户端和连接服务&#xff0c;包括大多数基于客户端/服务端工具实现的类似于TCP/IP的通信&#xff0c;主要功能是完成一些类似于连接处理、授权认证、安全方案等&#xff0c;在该层上还引入线程池的概念&#xff0c;为…

FL Studio21.2.0.3858免激活版安装下载

前阵子世界级电音盛会Tomorrowland在比利时如期举行&#xff0c;拉开了疫情下Rave文化复兴的帷幕。而国内&#xff0c;也推出了如《超感星电音》等电子音乐综艺&#xff0c;在节目上大家也更多地了解到了电子音乐的制作过程。节目中最被大家看好的制作人Carta所使用的FL Studio…

BUUCTF--[ACTF2020 新生赛]Include

目录 1、本题详解 2、延伸拓展 1、本题详解 访问题目链接 有一个tips的链接&#xff0c;我们点击 请求了file&#xff0c;内容是flag.php的内容&#xff1a;Can you find out the flag? 尝试请求一下index.php 并没有发现什么信息 flag.php也没发现什么 尝试爆破一下它的…

【Linux】:共享内存

共享内存 一.原理二.创建共享内存1.shmget2.写一个共享内存代码 三.进行通信1.各种接口2.各接口使用代码3.一次简单的通信四.共享内存的特点 一.原理 直接原理 共享内存顾名思义就是共同使用的一块空间。 很明显操作系统需要对这块内存进行管理&#xff0c;那么就避免不了先描…

MCU 的 TOP 15 图形GUI库:选择最适合你的图形用户界面(一)

在嵌入式系统开发中&#xff0c;选择一个合适的图形用户界面&#xff08;GUI&#xff09;库是至关重要的。在屏幕上显示的时候&#xff0c;使用现成的图形库&#xff0c;这样开发人员就不需要弄清楚底层任务&#xff0c;例如如何绘制像素、线条、形状&#xff0c;如果再高级一点…

一种全新且灵活的 Prompt 对齐优化技术

并非所有人都熟知如何与 LLM 进行高效交流。 一种方案是&#xff0c;人向模型对齐。 于是有了 「Prompt工程师」这一岗位&#xff0c;专门撰写适配 LLM 的 Prompt&#xff0c;从而让模型能够更好地生成内容。 而另一种更为有效的方案则是&#xff0c;让模型向人对齐。 这也是…

ES 查询语法-详解

文章目录 1.DSL查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.总结 1.3.精准查询1.3.1.term查询1.3.2.总结 1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff…

信号的处理时机(内核态,用户态,如何/为什么相互转换,内核空间,cpu寄存器),信号的处理流程详细介绍+抽象图解

目录 信号的处理时机 引入 思考 -- 什么时候才能算合适的时候呢? 用户态转为内核态 引入 内核地址空间 引入 思考 -- 进程为什么能切换成内核态呢? 虚拟地址空间 注意点 原理 (总结一下) 为什么如何进入内核态 引入 介绍 底层原理(int 80) cpu的寄存器 用…

LOIS: Looking Out of Instance Semanticsfor Visual Question Answering

目录 一、论文速读 1.1 摘要 1. 2 论文概要总结 二、论文精度 2.1 论文试图解决什么问题&#xff1f; 2.2 论文中提到的解决方案之关键是什么&#xff1f; 2.3 用于定量评估的数据集是什么&#xff1f;代码有没有开源&#xff1f; 2.4 这篇论文到底有什么贡献&#xff…

智能座舱架构与芯片- (15) 测试篇 下

三、持续集成与交付 3.1 自动化编译框架 在智能座舱软件中&#xff0c;分为上层应用软件和底层软件。有些上层应用软件是与指令集平台无关的&#xff0c;例如Java应用程序等&#xff0c;它们对所运行的CPU平台没有依赖性&#xff0c;可以很好的适配当前平台进行执行。而在底层…

基于WEB的停车场管理系统的设计和实现【附源码】

基于WEB的停车场管理系统的设计和实现 摘 要 随着现代社会的快速发展&#xff0c;人民生活水平快速提高&#xff0c;汽车的数量飞速增加&#xff0c;与此同时停车问题也越来越受到人们的关注&#xff0c;为了实现对停车场进行有效的管理&#xff0c;结合一些停车场的模式和现状…

机器学习与计算机视觉 D2

整合为学习笔记&#xff01;参考阅读了几位大佬的作品&#xff0c;已标注出处~ 机器学习的数学基础 线性与非线性变换 从几何意义上&#xff0c;线性变换表示的是直线的特性&#xff0c;符合两个性质: 变换前后零点不变&#xff0c;变换前后直线还是直线。 线性变换意味着可以…

亚马逊美国站买家号注册流程

注册亚马逊美国站买家号一般用邮箱及手机号注册就可以了&#xff0c;具体操作如下&#xff1a; 1、在浏览器里面输入亚马逊美国站的官网地址。 2、点击注册&#xff0c;输入姓名、邮箱或手机号、密码&#xff0c;然后进行验证邮箱或者手机号。如果是用的邮箱进行注册验证&…

c语言上机作业:给函数增加防御机制

1.题目 2.思路 1.首先&#xff0c;我们可以知道&#xff0c;我们必须先要把z求出来&#xff0c;但这里需要注意的是x&#xff0c;y并不包含了全部的定义域&#xff0c;所以我们必须先判断是否输入的数据满足条件。而这&#xff0c;就是我们所需要突破的函数的防御&#xff0c;…

单链表——OJ题(一)

目录 ​一.前言 二.移除链表元素 三.返回链表中间节点 四.链表中倒数第K个节点 五.合并两个有序链表 六.反转链表 七.链表分割 八.链表的回文结构 九.相交链表 十.环形链表 十一.环形链表&#xff08;二&#xff09; ​六.结语 一.前言 本文主要对平时的链表OJ进行…

Vue2+Vue3

文章目录 第 1 章&#xff1a;Vue 核心1、 Vue 简介1.官网2.介绍与描述3. Vue 的特点4. 与其它 JS 框架的关联5. Vue 周边库 2、初始Vue3、模板语法1、Vue模板语法有2大类:2、插值语法和指令语法 4、数据绑定1. 单向数据绑定2. 双向数据绑定 5、el与data的两种写法1.e1有2种写法…

专访特斯拉工程师杨硕:跟着机器人上天入地、探索地外行星丨智源独家

导读 十几岁时&#xff0c;他痴迷《终结者》&#xff0c;曾在百科全书中窥见卡内基梅隆大学机械臂的介绍&#xff0c;从而得知了研究机器人「圣地」的存在。 在CMU&#xff0c;他深耕足式机器人感知定位算法&#xff0c;期待未来涉足太空&#xff0c;走上火星。 在大疆&#xf…

水果音乐制作软件FL Studio21.2中文版新功能介绍

FL Studio21.2中文版&#xff0c;一般又称水果音乐制作软件。 FL Studio 21.2简称FL&#xff0c;全称FruityLoopsStudio&#xff0c;因此国人习惯叫它"水果"。它让你的计算机就像是全功能的录音室&#xff0c;大混音盘&#xff0c;非常先进的制作工具&#xff0c;让…

【C语言】数据结构——栈和队列实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;一、 栈1. 栈的概念及结构2. 栈的实现3. 实现代码3.1 定义结构体3.2 初始化栈3.3 销毁栈3.4 入栈3.5 出栈…

java io流中为什么使用缓冲流就能加快文件读写速度

FileInputStream的read方法底层确实是通过调用JDK层面的read方法&#xff0c;并且这个JDK层面的read方法底层是使用C语言编写的&#xff0c;以实现高效的文件读取功能。但是它会涉及多次内核态与操作系统交互。当我们使用FileInputStream的read方法读取文件时&#xff0c;首先会…