Este artículo incluye un resumen de los conocimientos básicos de estructuras de datos y algoritmos. Los estudiantes que necesiten saber sobre estructuras de datos y algoritmos, listas vinculadas, árboles binarios y otros conocimientos relacionados pueden verlos.
图
Se refiere a la relación de datos en la 逻辑结构
computadora Los datos en la computadora solo pueden almacenarse en la computadora mediante dos estructuras de almacenamiento, a saber 顺序存储
y 链式存储
.
1. Conocimientos básicos de grafos
- 1. Los puntos marcados en azul en la figura anterior se denominan vértices;
- 2. Las líneas que conectan los vértices se llaman aristas.
1. Definición de gráfico
Un gráfico se compone de un conjunto finito no vacío de vértices y un conjunto de aristas entre los vértices. Generalmente se denota como G(V,E), donde G representa el gráfico, V representa el conjunto de vértices y E representa el conjunto de aristas entre los vértices.
2. Clasificación de gráficos
1. Gráficos dirigidos y no dirigidos
- 1. Según la relación entre vértices y aristas;
- 2. Una arista entre dos vértices cualesquiera
没有方向
. Un grafo se llama无向图
arista, una arista se llama arista no dirigida;- 3. Una arista entre dos vértices cualesquiera
有方向
solo puede señalar de un vértice a otro. Un gráfico有向图
se llama borde dirigido y un borde se llama borde dirigido.
2. Grafos completos no dirigidos y grafos completos dirigidos
任意
Un vértice en un gráfico其他
tiene conexiones entre vértices.
3. Mapa de red
Las aristas conectadas entre los vértices en el gráfico son sí
有权重
, el peso de cada arista puede ser diferente, y la gráfica con el peso de la arista entre los vértices se llama网图
.
4. Gráficos conectados y desconectados
- 1. No hay conexión de borde entre algunos vértices y otros vértices en el gráfico, y no se puede hacer ninguna conexión indirecta a través de los bordes entre vértices.Este tipo de gráfico se llama
非连通图
, como se muestra en la Figura 1;- 2. Todos los vértices del gráfico se pueden conectar mediante aristas limitadas, es decir, todos los vértices están conectados, lo que se denomina gráfico de este tipo
连通图
, como se muestra en la Figura 2.
5. Subtramas
Un grafo que consta de cualquier conjunto de vértices y aristas conectadas en un grafo se llama grafo
子图
.
En segundo lugar, el almacenamiento de imágenes.
Tome las preguntas de la entrevista de Kuaishou como ejemplo:
Análisis del tema:
- 1.题目要求:设计一种数据结构,将上面的图存到计算机的内存中;
- 2.上图是一个
有向网图
;- 3.图中有
顶点数据
[v0 v1 v2 v3 v4],边数据
[1(v3->v4) 2(v2->v0) 3(v1->v2) 5(v2->v3) 6(v0->v4) 9(v1->v0)];
思考:如何将顶点和边存到内存中,并且满足图中各顶点之间的关系呢?
(一)顺序存储--邻接矩阵
1.邻接矩阵
(1)图是一个无向图、并且边无权重时
- 1.用一个
一维数组
存储顶点
信息;- 2.用一个
二维数组
存储顶点之间的边
信息,顶点之间有连接用1表示,无连接用0表示;
上图存储边信息的二维数组矩阵有如下特征:
- 1.第i行第j列的位置存储的是顶点Vi和顶点Vj的边信息,如果Vi和Vj
有连接
关系,则i行j行记为1
,二维数组的对应位置存储1,否则记为0
;- 2.矩阵的
对角线
上存的数据为0
,即任意顶点和自己之间是没有连接关系的。- 3.矩阵中的数据以对角线
对称
,即无向图
中Vi和Vj之间的边和Vj和Vi之间的边是相等
的。
(2)图是一个有向图,并且边无权重时
这种情况与(1)的区别主要是图是一个
有向图
,二维数组矩阵中Vi和Vj之间的边和Vj和Vi之间的边是不相等,即Vi指向Vj,但Vj不一定指向Vi.
(3)图是一个有向图,并且有权重时
- 1.这种情况和(2)的区别就是
有权重
,所以二维数组存的数据是边的权重值;- 2.由于权重值有可能是1,所以如果顶点之间的没有边信息,对应的二维数组存的值可以为一个异常的数据,如
无穷大
。
通过上面的分析,我们得到的存储图的顶点与顶点关系的二维数组矩阵即为邻接矩阵。
2.代码实现
1.定义一些状态值和数据类型
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 //最大顶点数,应由用户定义
#define INFINITYC 0
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
typedef char VertexType; //顶点类型
typedef int EdgeType; //边上的权值类型
复制代码
2.存储结构设计
typedef struct
{
VertexType vexs[MAXVEX]; //顶点表
EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵,可看作边表
int numNodes, numEdges; //图中当前的顶点数和边数
}MGraph;
复制代码
3.存储实现
void CreateMGraph(MGraph *G){
int i,j,k,w;
printf("输入顶点数和边数:\n");
//1. 输入顶点数/边数
scanf("%d,%d",&G->numNodes,&G->numEdges);
printf("顶点数:%d,边数:%d\n",G->numNodes,G->numEdges);
//2.输入顶点信息/顶点表
for(int i = 0; i< G->numNodes;i++)
{
getchar();
scanf("%c",&G->vexs[i]);
}
printf("顶点数据:\n");
for (i = 0; i<G->numNodes; i++) {
printf("%c ",G->vexs[i]);
}
printf("\n");
//3.初始化邻接矩阵
for(i = 0; i < G->numNodes;i++)
for(j = 0; j < G->numNodes;j++)
G->arc[i][j] = INFINITYC;
//4.输入边表信息
for(k = 0; k < G->numEdges;k++){
printf("输入边(vi,vj)上的下标i,下标j,权w\n");
scanf("%d,%d,%d",&i,&j,&w);
G->arc[i][j] = w;
//如果无向图,矩阵对称;
G->arc[j][i] = G->arc[i][j];
}
//5.打印邻接矩阵
for (int i = 0; i < G->numNodes; i++) {
printf("\n");
for (int j = 0; j < G->numNodes; j++) {
printf("%d ",G->arc[i][j]);
}
}
printf("\n");
}
复制代码
4.调试
5.邻接矩阵的缺点
- 1.如上面的图,有5个顶点,但却只有一条边信息;
- 2.但在顺序存储中,需要申请5x5的二维数组矩阵空间来存储这一条边信息;
- 3.这就导致了大量的
空间浪费
。
(二)链式存储--邻接表
1.邻接表
- 1.创建一个
一维顶点数组
存储顶点信息;- 2.
顶点数组
存的元素是一个顶点数据的结点
,顶点的数据结构中有一个指针指向边信息
,边信息
是一个单向链表
,这个单向链表存储了所有与顶点有连接的顶点的边信息
;- 3.边信息的单向链表保存的是顶点与其他顶点之间的边数据,它们
没有顺序关系
。
2.代码实现
以如上例子实现邻接表下的链式存储
1.定义一些状态值和数据类型
#define M 100
#define true 1
#define false 0
typedef char Element;
typedef int BOOL;
复制代码
2.定义结点和存储结构
//邻接表的边结点
typedef struct Node{
int adj_vex_index; //边信息对应的顶点在数组中的下标
Element data; //边的权重值
struct Node * next; //边指针:指向下一个边结点
}EdgeNode;
//顶点结点
typedef struct vNode{
Element data; //顶点数据
EdgeNode * firstedge; //顶点下一个是谁?
}VertexNode, Adjlist[M];
//总图的一些信息
typedef struct Graph{
Adjlist adjlist; //顶点表
int arc_num; //边数
int node_num; //顶点数
BOOL is_directed; //是不是有向图
}Graph, *GraphLink;
复制代码
3.存储实现
void creatGraph(GraphLink *g){
int i,j,k;
EdgeNode *p;
//1. 顶点,边,是否有向
printf("输入顶点数目,边数和有向?:\n");
scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
//2.顶点表
printf("输入顶点信息:\n");
for (i = 0; i < (*g)->node_num; i++) {
getchar();
scanf("%c", &(*g)->adjlist[i].data);
//创建顶点时,顶点数据指向边信息此时还没有,所以指向null
(*g)->adjlist[i].firstedge = NULL;
}
//3.边信息
printf("输入边信息:\n");
for (k = 0; k < (*g)->arc_num; k++){
getchar();
scanf("%d %d", &i, &j);
//①新建一个节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
//②弧头的下标
p->adj_vex_index = j;
//③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
p->next = (*g)->adjlist[i].firstedge;
//④将顶点数组[i].firstedge 设置为p
(*g)->adjlist[i].firstedge = p;
if(!(*g)->is_directed)
{
// j -----> i
//①新建一个节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
//②弧头的下标i
p->adj_vex_index = i;
//③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
p->next = (*g)->adjlist[j].firstedge;
//④将顶点数组[i].firstedge 设置为p
(*g)->adjlist[j].firstedge = p;
}
}
}
复制代码
- 1.输入
顶点个数
、边数
、是否是有向图
等信息;- 2.
顶点
存入一维数组
中,一维数组存的是一个带数据域
和指针域
的结构体,数据域
存储顶点数据
,指针域
指向存储边信息
的单向链表;- 3.输入边信息,i是当前
顶点下标
,j是连接顶点
在顶点数组中的下标
;- 4.输入后
创建结点
,记录权重值、顶点的下标j、指针域指向等;- 5. Para determinar si es
有向图
, si es un grafo no dirigido, es necesario determinar la relación entre j como vértice original e i como vértice puntiagudo, y crear la relación entre los dos vértices de j e i, es decir,反向关系
.
4. Recorrer e imprimir
void putGraph(GraphLink g){
int i;
printf("邻接表中存储信息:\n");
//遍历一遍顶点坐标,每个再进去走一次
for (i = 0; i < g->node_num; i++) {
EdgeNode * p = g->adjlist[i].firstedge;
while (p) {
printf("%c->%c ", g->adjlist[i].data, g->adjlist[p->adj_vex_index].data);
p = p->next;
}
printf("\n");
}
}
复制代码
5. Depurar
/*
邻接表实现图的存储
输入顶点数目,边数和有向?:
4 5 0
输入顶点信息:
0 1 2 3
输入边信息:
0 1 0 2 0 3 2 1 2 3
邻接表中存储信息:
0->3 0->2 0->1
1->2 1->0
2->3 2->1 2->0
3->2 3->0
*/
/*
邻接表实现图的存储
输入顶点数目,边数和有向?:
4 5 1
输入顶点信息:
0 1 2 3
输入边信息:
1 0 1 2 2 1 2 0 0 3
邻接表中存储信息:
0->3
1->2 1->0
2->0 2->1
*/
GraphLink g = (Graph *)malloc(sizeof(Graph));
creatGraph(&g);
putGraph(g);
复制代码
almacenamiento de gráficos no dirigido
Almacenamiento de gráficos dirigidos
4. Resumen
- 1. Un gráfico es un tipo de datos informáticos
逻辑结构
que se pueden utilizar顺序存储
o utilizar链式存储
;- 2. El almacenamiento secuencial de gráficos se
邻接矩阵
implementa utilizando;- 3. El almacenamiento de prueba de cadena del gráfico se
邻接表
implementa utilizando.