18 数据结构与算法之树与图

在上一篇中,我们讨论了 队列 这两种重要的数据结构。本篇将深入探讨 ,这两者在计算机科学中扮演着至关重要的角色,尤其是在表示层次结构和复杂关系时。

5.3.1 树

1. 什么是树?

树是一种非线性数据结构,由节点(nodes)和边(edges)组成。树具有以下特征:

  • 有一个根节点(root),其他节点通过边连接。
  • 每个节点可以有零个或多个子节点(children)。
  • 除根节点外,每个节点都有一个唯一的父节点(parent)。
  • 树形结构没有循环。

2. 树的基本术语

  • 高度(Height):从树的根节点到最叶子节点的最长路径上的边数。
  • 深度(Depth):从根节点到某个节点的路径长度。
  • 叶子节点(Leaf Node):没有子节点的节点。

3. 树的类型

  • 二叉树(Binary Tree):每个节点最多有两个孩子,通常称为左孩(left child)和右孩(right child)。
  • 二叉搜索树(Binary Search Tree, BST):左孩子的值小于父节点,右孩子的值大于父节点。
  • AVL树:一种自平衡的二叉搜索树,保证操作的时间复杂度为 $O(\log n)$。
  • 红黑树:另一种自平衡的二叉搜索树,具有颜色标记的节点来保持平衡。

4. 树的基本操作

以下是一些基本操作的实现示例(以二叉树为例):

插入节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct Node {
int data;
struct Node* left;
struct Node* right;
} Node;

Node* insert(Node* root, int data) {
if (root == NULL) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
if (data < root->data) {
root->left = insert(root->left, data);
} else {
root->right = insert(root->right, data);
}
return root;
}

遍历树

树的遍历可分为前序、中序和后序遍历。

1
2
3
4
5
6
7
void inorder(Node* root) {
if (root != NULL) {
inorder(root->left);
printf("%d ", root->data);
inorder(root->right);
}
}

5.3.2 图

1. 什么是图?

图是一种由节点(顶点)和连接这些节点的边构成的数据结构。图可以表示复杂的关系,并具有以下基本特征:

  • 节点通过边连接,边可以是有向的或无向的。
  • 一个图可以包含环(cycles),也可以是无环(如树)。

2. 图的类型

  • 有向图(Directed Graph):边有方向,有序对 (u, v) 表示从顶点 $u$ 到 $v$ 的一条边。
  • 无向图(Undirected Graph):边没有方向,连接两个顶点的边可以用无序对 {u, v} 表示。
  • 加权图(Weighted Graph):边上有权值,常用来表示成本或距离。

3. 图的存储方式

图一般使用两种方式存储:

  • 邻接矩阵(Adjacency Matrix):使用一个二维数组存储节点间的关系。
  • 邻接链表(Adjacency List):使用链表存储每个节点的边的信息,适合稀疏图。

邻接链表的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct Edge {
int destination;
struct Edge* next;
} Edge;

typedef struct Vertex {
int data;
Edge* edges;
} Vertex;

typedef struct Graph {
int numVertices;
Vertex* vertices;
} Graph;

4. 图的基本操作

深度优先搜索(DFS)

1
2
3
4
5
6
7
8
9
10
11
12
void DFS(Graph* graph, int vertex, int* visited) {
visited[vertex] = 1;
printf("%d ", graph->vertices[vertex].data);

Edge* edge = graph->vertices[vertex].edges;
while (edge != NULL) {
if (!visited[edge->destination]) {
DFS(graph, edge->destination, visited);
}
edge = edge->next;
}
}

广度优先搜索(BFS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void BFS(Graph* graph, int startVertex) {
Queue* queue = createQueue();
int* visited = (int*)malloc(graph->numVertices * sizeof(int));
memset(visited, 0, graph->numVertices * sizeof(int));

enqueue(queue, startVertex);
visited[startVertex] = 1;

while (!isEmpty(queue)) {
int currentVertex = dequeue(queue);
printf("%d ", graph->vertices[currentVertex].data);

Edge* edge = graph->vertices[currentVertex].edges;
while (edge != NULL) {
if (!visited[edge->destination]) {
enqueue(queue, edge->destination);
visited[edge->destination] = 1;
}
edge = edge->next;
}
}
}

5. 总结

本篇介绍了 两种重要的数据结构,讲解了它们的基本概念、操作及实现示例。树结构适合表示层级关系,而图结构则用于表示更为复杂的连接关系。在实际应用中,选择合适的数据结构是提升程序性能和可维护性的关键。

接下来,我们将进入经典算法的实现部分,这将帮助加深理解数据结构在解决问题中的应用,以及如何优化算法效率。

18 数据结构与算法之树与图

https://zglg.work/cplusplus-one/18/

作者

IT教程网(郭震)

发布于

2024-08-10

更新于

2024-08-10

许可协议

分享转发

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论