树(Tree)是一种非线性的数据结构,由若干个节点(Node)组成。树的定义包括以下几个术语:
术语 | 解释 |
---|---|
节点(Node) | 树中的每个元素称为节点。每个节点包含一个值和指向其他节点的指针或引用。 |
根节点(Root) | 树的顶层节点称为根节点,它没有父节点。 |
子节点(Child) | 一个节点可以有零个或多个子节点,子节点是其父节点的直接后继。 |
父节点(Parent) | 一个节点的直接上级节点称为父节点。 |
兄弟节点(Sibling) | 具有相同父节点的节点互为兄弟节点。 |
叶节点(Leaf) | 没有子节点的节点称为叶节点或终端节点。 |
子树(Subtree) | 树中的任意一个节点及其所有后代节点构成一个子树。 |
深度(Depth) | 根节点到某个节点的路径长度称为该节点的深度。 |
高度(Height) | 树中节点的最大深度称为树的高度。 |
节点的度(Degree) | 一个节点拥有的子节点数量称为节点的度。 |
树的度(Degree) | 树中节点的最大度称为树的度。 |
Tip:树的定义和术语为我们理解树结构提供了基础概念。根据节点之间的关系和属性,树的形态和特性可以有很多种类,如二叉树、二叉搜索树、平衡树等。了解这些概念和术语有助于我们更好地理解树结构以及相关的操作和算法。
树(Tree)作为一种常见的数据结构,具有以下特点和性质:
特点与性质 | 解释 |
---|---|
非线性结构 | 树是一种非线性的数据结构,与线性结构(如数组和链表)相对。树的节点之间通过边连接,形成分层关系。 |
层级关系 | 树中的节点按照层级进行组织,根节点位于最顶层,其他节点依次排列在下方的层级。 |
有且仅有一个根节点 | 树中只有一个根节点,它是整个树的起始节点,没有父节点。 |
子节点和父节点 | 每个节点可以有零个或多个子节点,每个节点除了根节点之外都有一个父节点。 |
分支结构 | 节点之间的连接称为边,用于表示节点之间的关系。从根节点到任意节点都有唯一的路径。 |
无环结构 | 树是无环的,即不存在节点之间的循环路径。 |
唯一路径 | 树中的任意两个节点之间有且仅有唯一的路径。 |
深度和高度 | 节点的深度是从根节点到该节点的路径长度,树的高度是所有节点深度的最大值。 |
子树 | 树中的任意一个节点及其所有后代节点构成一个子树。 |
叶节点 | 没有子节点的节点称为叶节点或终端节点。 |
Tip:树的特点和性质使其具有良好的层级结构,适用于许多实际应用场景,如文件系统、数据库索引、组织结构等。
常见的树结构包括以下几种:
这些常见的树结构在不同场景下具有不同的应用和特点。二叉搜索树适用于快速的查找、插入和删除操作;平衡树解决了二叉搜索树在频繁插入和删除时可能导致的不平衡问题;B树和Trie树适用于大规模数据的存储和检索。
深度优先遍历(Depth-First Search,DFS)是一种用于遍历或搜索树或图的算法。在树的遍历中,DFS按照深度优先的顺序遍历树的节点,从根节点开始,先访问当前节点,然后递归地访问其左子树和右子树。DFS有三种常见的遍历方式:前序遍历、中序遍历和后序遍历。下面是这三种遍历方式的C语言代码示例:
struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
};
void preorderTraversal(struct TreeNode* root) {
if (root == NULL) return;
printf("%d ", root->val); // 访问当前节点
preorderTraversal(root->left); // 遍历左子树
preorderTraversal(root->right); // 遍历右子树
}
void inorderTraversal(struct TreeNode* root) {
if (root == NULL) return;
inorderTraversal(root->left); // 遍历左子树
printf("%d ", root->val); // 访问当前节点
inorderTraversal(root->right); // 遍历右子树
}
void postorderTraversal(struct TreeNode* root) {
if (root == NULL) return;
postorderTraversal(root->left); // 遍历左子树
postorderTraversal(root->right); // 遍历右子树
printf("%d ", root->val); // 访问当前节点
}
Tip:这些深度优先遍历的代码示例可以帮助你理解DFS算法的具体实现。根据不同的遍历顺序,可以灵活选择合适的方式来处理树的节点。
广度优先遍历(Breadth-First Search,BFS)是一种用于遍历或搜索树或图的算法。BFS按照广度优先的顺序遍历树的节点,即逐层地访问节点。BFS从根节点开始,先访问根节点,然后按照层级顺序依次访问每一层的节点,直到遍历完所有节点。下面是BFS的C语言代码示例:
#include <stdio.h>
#define MAX_QUEUE_SIZE 100
struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
};
// 队列结构
struct Queue {
struct TreeNode* data[MAX_QUEUE_SIZE];
int front; // 队头指针
int rear; // 队尾指针
};
// 初始化队列
void initQueue(struct Queue* queue) {
queue->front = 0;
queue->rear = 0;
}
// 入队操作
void enqueue(struct Queue* queue, struct TreeNode* node) {
if (queue->rear >= MAX_QUEUE_SIZE) {
printf("Queue is full.\n");
return;
}
queue->data[queue->rear] = node;
queue->rear++;
}
// 出队操作
struct TreeNode* dequeue(struct Queue* queue) {
if (queue->front == queue->rear) {
printf("Queue is empty.\n");
return NULL;
}
struct TreeNode* node = queue->data[queue->front];
queue->front++;
return node;
}
// 广度优先遍历
void breadthFirstTraversal(struct TreeNode* root) {
if (root == NULL) return;
struct Queue queue;
initQueue(&queue);
enqueue(&queue, root); // 根节点入队
while (queue.front != queue.rear) {
struct TreeNode* node = dequeue(&queue); // 出队当前节点
printf("%d ", node->val); // 访问当前节点
// 将当前节点的左右子节点入队
if (node->left) {
enqueue(&queue, node->left);
}
if (node->right) {
enqueue(&queue, node->right);
}
}
}
int main() {
// 构建一棵二叉树作为示例
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = 1;
root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->left->val = 2;
root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->val = 3;
root->left->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->left->left->val = 4;
root->left->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->left->right->val = 5;
root->right->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->left->val = 6;
root->right->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->right->val = 7;
// 广度优先遍历
breadthFirstTraversal(root);
return 0;
}
以上代码示例演示了如何使用队列来实现广度优先遍历。通过维护一个队列,将当前节点的子节点按顺序入队,从而实现按层级遍历的效果。
树的插入操作的时间复杂度和空间复杂度取决于树的类型和结构。以下是常见树结构的插入操作的时间复杂度和空间复杂度:
Tip:以上的时间复杂度和空间复杂度是基于平衡树的理想情况。在实际应用中,树的平衡性可能会受到数据的分布和插入顺序的影响,导致插入操作的时间复杂度稍有不同。因此,在选择树的类型和实现插入操作时,需要综合考虑数据特点和性能需求。
树的删除操作的时间复杂度和空间复杂度取决于树的类型和结构。以下是常见树结构的删除操作的时间复杂度和空间复杂度:
树的查找操作的时间复杂度和空间复杂度取决于树的类型和结构。以下是常见树结构的查找操作的时间复杂度和空间复杂度:
图(Graph)是由节点(Vertex)和边(Edge)组成的非线性数据结构。图中的节点表示实体,边表示节点之间的关系或连接。
术语 | 解释 |
---|---|
节点(Vertex) | 也称为顶点或结点,表示图中的实体。 |
边(Edge) | 表示节点之间的连接关系,可以是有向的(指定方向)或无向的(不指定方向)。 |
权重(Weight) | 边可以带有权重,表示节点之间的关联程度或距离。 |
邻接(Adjacent) | 两个节点之间存在边,则它们被称为邻接节点。 |
入度(In-degree) | 有向图中,指向某个节点的边的数量。 |
出度(Out-degree) | 有向图中,从某个节点出发的边的数量。 |
路径(Path) | 图中节点的序列,节点之间通过边连接。 |
环(Cycle) | 路径中起始节点和结束节点相同的路径。 |
连通性(Connectivity) | 图中节点之间是否存在路径,决定了图的连通性。 |
子图(Subgraph) | 由图中的一部分节点和边组成的图。 |
无向图(Undirected Graph) | 边没有方向性,节点之间的关系是双向的。 |
有向图(Directed Graph) | 边具有方向性,节点之间的关系是单向的。 |
图可以用于表示各种实际问题,如社交网络、路网、电路等。
有向图(Directed Graph)和无向图(Undirected Graph)是图(Graph)中两种常见的类型。
比较:
图可以使用多种方式进行表示,以下是几种常见的图表示方法:
以下是使用深度优先遍历(DFS)算法遍历图的示例代码,使用C语言实现:
#include <stdio.h>
#include <stdbool.h>
#define MAX_VERTICES 100
// 邻接表节点的结构
struct ListNode {
int vertex;
struct ListNode* next;
};
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
struct ListNode* adjList[MAX_VERTICES]; // 邻接表数组
bool visited[MAX_VERTICES]; // 标记节点是否已访问
};
// 初始化图
void initGraph(struct Graph* graph, int numVertices) {
graph->numVertices = numVertices;
// 初始化邻接表和visited数组
for (int i = 0; i < numVertices; ++i) {
graph->adjList[i] = NULL;
graph->visited[i] = false;
}
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
// 创建新的邻接表节点
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->vertex = dest;
newNode->next = graph->adjList[src];
graph->adjList[src] = newNode;
}
// 深度优先遍历
void DFS(struct Graph* graph, int vertex) {
// 标记当前节点为已访问
graph->visited[vertex] = true;
printf("%d ", vertex);
// 递归访问当前节点的邻接节点
struct ListNode* adjNode = graph->adjList[vertex];
while (adjNode != NULL) {
int adjVertex = adjNode->vertex;
if (!graph->visited[adjVertex]) {
DFS(graph, adjVertex);
}
adjNode = adjNode->next;
}
}
// 测试DFS遍历
int main() {
struct Graph graph;
int numVertices = 6;
initGraph(&graph, numVertices);
// 添加边
addEdge(&graph, 0, 1);
addEdge(&graph, 0, 2);
addEdge(&graph, 1, 3);
addEdge(&graph, 2, 4);
addEdge(&graph, 3, 4);
addEdge(&graph, 3, 5);
printf("DFS traversal: ");
DFS(&graph, 0);
return 0;
}
以上代码演示了如何使用深度优先遍历算法遍历图。通过创建一个邻接表来表示图的连接关系,并使用一个visited数组来标记节点是否已被访问。在DFS函数中,首先标记当前节点为已访问,并输出节点的值,然后递归地访问当前节点的邻接节点,直到所有节点都被访问过。输出的结果为:DFS traversal: 0 1 3 4 2 5,表示深度优先遍历图从节点0开始的路径。
以下是使用广度优先遍历(BFS)算法遍历图的示例代码,使用C语言实现:
#include <stdio.h>
#include <stdbool.h>
#define MAX_VERTICES 100
#define QUEUE_SIZE 100
// 邻接表节点的结构
struct ListNode {
int vertex;
struct ListNode* next;
};
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
struct ListNode* adjList[MAX_VERTICES]; // 邻接表数组
bool visited[MAX_VERTICES]; // 标记节点是否已访问
};
// 初始化图
void initGraph(struct Graph* graph, int numVertices) {
graph->numVertices = numVertices;
// 初始化邻接表和visited数组
for (int i = 0; i < numVertices; ++i) {
graph->adjList[i] = NULL;
graph->visited[i] = false;
}
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
// 创建新的邻接表节点
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->vertex = dest;
newNode->next = graph->adjList[src];
graph->adjList[src] = newNode;
}
// 广度优先遍历
void BFS(struct Graph* graph, int startVertex) {
bool visited[MAX_VERTICES] = { false }; // 用于标记节点是否已访问
int queue[QUEUE_SIZE]; // 队列用于存储待访问的节点
int front = 0; // 队列的前指针
int rear = 0; // 队列的后指针
// 将起始节点入队并标记为已访问
queue[rear++] = startVertex;
visited[startVertex] = true;
// 开始BFS遍历
while (front < rear) {
// 出队一个节点并输出
int currentVertex = queue[front++];
printf("%d ", currentVertex);
// 遍历当前节点的邻接节点
struct ListNode* adjNode = graph->adjList[currentVertex];
while (adjNode != NULL) {
int adjVertex = adjNode->vertex;
// 如果邻接节点未被访问,则将其入队并标记为已访问
if (!visited[adjVertex]) {
queue[rear++] = adjVertex;
visited[adjVertex] = true;
}
adjNode = adjNode->next;
}
}
}
// 测试BFS遍历
int main() {
struct Graph graph;
int numVertices = 6;
initGraph(&graph, numVertices);
// 添加边
addEdge(&graph, 0, 1);
addEdge(&graph, 0, 2);
addEdge(&graph, 1, 3);
addEdge(&graph, 2, 4);
addEdge(&graph, 3, 4);
addEdge(&graph, 3, 5);
printf("BFS traversal: ");
BFS(&graph, 0);
return 0;
}
以上代码演示了如何使用广度优先遍历算法遍历图。通过创建一个邻接表来表示图的连接关系,并使用一个visited数组和队列来辅助遍历。在BFS函数中,首先将起始节点入队并标记为已访问,然后通过不断出队和入队的操作,遍历当前节点的邻接节点,直到队列为空。输出的结果为:BFS traversal: 0 1 2 3 4 5,表示广度优先遍历图从节点0开始的路径。
常见的两种图的最短路径算法是迪杰斯特拉算法(Dijkstra’s algorithm)和弗洛伊德算法(Floyd’s algorithm)。
#include <stdio.h>
#include <stdbool.h>
#define MAX_VERTICES 100
#define INF 9999
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
};
// 迪杰斯特拉算法
void Dijkstra(struct Graph* graph, int startVertex) {
int dist[MAX_VERTICES]; // 存储起始节点到各个节点的最短距离
bool visited[MAX_VERTICES]; // 标记节点是否已被访问
// 初始化距离数组和visited数组
for (int i = 0; i < graph->numVertices; ++i) {
dist[i] = INF;
visited[i] = false;
}
// 起始节点距离设为0
dist[startVertex] = 0;
// 找到剩余节点中距离最小的节点
for (int i = 0; i < graph->numVertices - 1; ++i) {
int minDist = INF;
int minIndex = -1;
// 遍历节点找到距离最小的节点
for (int j = 0; j < graph->numVertices; ++j) {
if (!visited[j] && dist[j] < minDist) {
minDist = dist[j];
minIndex = j;
}
}
// 将找到的最小距离节点标记为已访问
visited[minIndex] = true;
// 更新与该节点相邻节点的最短距离
for (int j = 0; j < graph->numVertices; ++j) {
if (!visited[j] && graph->adjacencyMatrix[minIndex][j] && dist[minIndex] != INF && dist[minIndex] + graph->adjacencyMatrix[minIndex][j] < dist[j]) {
dist[j] = dist[minIndex] + graph->adjacencyMatrix[minIndex][j];
}
}
}
// 输出最短路径
printf("Shortest paths from vertex %d:\n", startVertex);
for (int i = 0; i < graph->numVertices; ++i) {
printf("%d -> %d: %d\n", startVertex, i, dist[i]);
}
}
// 测试迪杰斯特拉算法
int main() {
struct Graph graph;
int numVertices = 6;
graph.numVertices = numVertices;
// 初始化邻接矩阵
for (int i = 0; i < numVertices; ++i) {
for (int j = 0; j < numVertices; ++j) {
graph.adjacencyMatrix[i][j] = INF;
}
}
// 添加图的边
graph.adjacencyMatrix[0][1] = 2;
graph.adjacencyMatrix[0][2] = 4;
graph.adjacencyMatrix[1][2] = 1;
graph.adjacencyMatrix[1][3] = 7;
graph.adjacencyMatrix[2][4] = 3;
graph.adjacencyMatrix[3][4] = 1;
graph.adjacencyMatrix[3][5] = 5;
graph.adjacencyMatrix[4][5] = 2;
int startVertex = 0; // 起始节点
Dijkstra(&graph, startVertex);
return 0;
}
#include <stdio.h>
#define MAX_VERTICES 100
#define INF 9999
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
};
// 弗洛伊德算法
void Floyd(struct Graph* graph) {
int dist[MAX_VERTICES][MAX_VERTICES]; // 存储任意两个节点之间的最短距离
// 初始化距离矩阵
for (int i = 0; i < graph->numVertices; ++i) {
for (int j = 0; j < graph->numVertices; ++j) {
dist[i][j] = graph->adjacencyMatrix[i][j];
}
}
// 计算最短路径
for (int k = 0; k < graph->numVertices; ++k) {
for (int i = 0; i < graph->numVertices; ++i) {
for (int j = 0; j < graph->numVertices; ++j) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
// 输出最短路径
printf("Shortest paths between all pairs of vertices:\n");
for (int i = 0; i < graph->numVertices; ++i) {
for (int j = 0; j < graph->numVertices; ++j) {
printf("%d -> %d: %d\n", i, j, dist[i][j]);
}
}
}
// 测试弗洛伊德算法
int main() {
struct Graph graph;
int numVertices = 4;
graph.numVertices = numVertices;
// 初始化邻接矩阵
for (int i = 0; i < numVertices; ++i) {
for (int j = 0; j < numVertices; ++j) {
graph.adjacencyMatrix[i][j] = INF;
}
}
// 添加图的边
graph.adjacencyMatrix[0][1] = 3;
graph.adjacencyMatrix[0][2] = 6;
graph.adjacencyMatrix[1][0] = 3;
graph.adjacencyMatrix[1][2] = 2;
graph.adjacencyMatrix[1][3] = 1;
graph.adjacencyMatrix[2][0] = 6;
graph.adjacencyMatrix[2][1] = 2;
graph.adjacencyMatrix[2][3] = 4;
graph.adjacencyMatrix[3][1] = 1;
graph.adjacencyMatrix[3][2] = 4;
Floyd(&graph);
return 0;
}
图的最小生成树算法主要用于找到一个连通图的最小生成树,即连接图中所有节点的边的集合,且边的权重之和最小。常见的最小生成树算法包括普里姆算法和克鲁斯卡尔算法。
#include <stdio.h>
#define MAX_VERTICES 100
#define INF 9999
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
};
// 普里姆算法
void Prim(struct Graph* graph) {
int selected[MAX_VERTICES]; // 记录节点是否已加入生成树
int parent[MAX_VERTICES]; // 记录节点的父节点
int minWeight[MAX_VERTICES]; // 记录节点与生成树的最小权重
// 初始化
for (int i = 0; i < graph->numVertices; ++i) {
selected[i] = 0;
minWeight[i] = INF;
}
// 设置起始节点
int startVertex = 0;
minWeight[startVertex] = 0;
parent[startVertex] = -1;
// 生成最小生成树
for (int i = 0; i < graph->numVertices - 1; ++i) {
// 选择最小权重的节点
int minWeightIndex = -1;
for (int j = 0; j < graph->numVertices; ++j) {
if (!selected[j] && (minWeightIndex == -1 || minWeight[j] < minWeight[minWeightIndex])) {
minWeightIndex = j;
}
}
// 将节点加入生成树
selected[minWeightIndex] = 1;
// 更新相邻节点的权重
for (int j = 0; j < graph->numVertices; ++j) {
if (!selected[j] && graph->adjacencyMatrix[minWeightIndex][j] != INF && graph->adjacencyMatrix[minWeightIndex][j] < minWeight[j]) {
parent[j] = minWeightIndex;
minWeight[j] = graph->adjacencyMatrix[minWeightIndex][j];
}
}
}
// 输出最小生成树
printf("Minimum Spanning Tree:\n");
for (int i = 1; i < graph->numVertices; ++i) {
printf("%d - %d\n", parent[i], i);
}
}
// 测试普里姆算法
int main() {
struct Graph graph;
int numVertices = 5;
graph.numVertices = numVertices;
// 初始化邻接矩阵
for (int i = 0; i < numVertices; ++i) {
for (int j = 0; j < numVertices; ++j) {
graph.adjacencyMatrix[i][j] = INF;
}
}
// 添加图的边
graph.adjacencyMatrix[0][1] = 2;
graph.adjacencyMatrix[0][3] = 6;
graph.adjacencyMatrix[1][0] = 2;
graph.adjacencyMatrix[1][2] = 3;
graph.adjacencyMatrix[1][3] = 8;
graph.adjacencyMatrix[1][4] = 5;
graph.adjacencyMatrix[2][1] = 3;
graph.adjacencyMatrix[2][4] = 7;
graph.adjacencyMatrix[3][0] = 6;
graph.adjacencyMatrix[3][1] = 8;
graph.adjacencyMatrix[4][1] = 5;
graph.adjacencyMatrix[4][2] = 7;
Prim(&graph);
return 0;
}
#include <stdio.h>
#define MAX_EDGES 100
#define MAX_VERTICES 100
// 边的结构
struct Edge {
int source; // 边的起始节点
int destination; // 边的目标节点
int weight; // 边的权重
};
// 图的结构
struct Graph {
int numVertices; // 图中节点的数量
int numEdges; // 图中边的数量
struct Edge edges[MAX_EDGES]; // 图中的边
};
// 比较函数,用于边的排序
int compare(const void* a, const void* b) {
struct Edge* edgeA = (struct Edge*)a;
struct Edge* edgeB = (struct Edge*)b;
return edgeA->weight - edgeB->weight;
}
// 查找节点的根节点
int findRoot(int parent[], int vertex) {
while (parent[vertex] != -1) {
vertex = parent[vertex];
}
return vertex;
}
// 克鲁斯卡尔算法
void Kruskal(struct Graph* graph) {
int parent[MAX_VERTICES]; // 记录节点的父节点
struct Edge result[MAX_VERTICES]; // 记录最小生成树的边
// 初始化父节点
for (int i = 0; i < graph->numVertices; ++i) {
parent[i] = -1;
}
// 对边进行排序
qsort(graph->edges, graph->numEdges, sizeof(struct Edge), compare);
int edgeCount = 0; // 记录已选择的边的数量
int i = 0; // 记录当前考虑的边的索引
// 选择边,直到生成树包含了所有节点为止
while (edgeCount < graph->numVertices - 1) {
struct Edge currentEdge = graph->edges[i];
int sourceRoot = findRoot(parent, currentEdge.source);
int destinationRoot = findRoot(parent, currentEdge.destination);
// 如果边的起始节点和目标节点不在同一个连通分量中,则选择该边
if (sourceRoot != destinationRoot) {
result[edgeCount++] = currentEdge;
parent[sourceRoot] = destinationRoot;
}
i++;
}
// 输出最小生成树
printf("Minimum Spanning Tree:\n");
for (int i = 0; i < edgeCount; ++i) {
printf("%d - %d\n", result[i].source, result[i].destination);
}
}
// 测试克鲁斯卡尔算法
int main() {
struct Graph graph;
int numVertices = 5;
int numEdges = 7;
graph.numVertices = numVertices;
graph.numEdges = numEdges;
// 添加图的边
graph.edges[0].source = 0;
graph.edges[0].destination = 1;
graph.edges[0].weight = 2;
graph.edges[1].source = 0;
graph.edges[1].destination = 3;
graph.edges[1].weight = 6;
graph.edges[2].source = 1;
graph.edges[2].destination = 2;
graph.edges[2].weight = 3;
graph.edges[3].source = 1;
graph.edges[3].destination = 3;
graph.edges[3].weight = 8;
graph.edges[4].source = 1;
graph.edges[4].destination = 4;
graph.edges[4].weight = 5;
graph.edges[5].source = 2;
graph.edges[5].destination = 4;
graph.edges[5].weight = 7;
graph.edges[6].source = 3;
graph.edges[6].destination = 4;
graph.edges[6].weight = 9;
Kruskal(&graph);
return 0;
}
经典面试题1:给定一个二叉树,判断它是否是对称的,即左子树和右子树镜像对称。 解题思路: 可以使用递归的方式来解决该问题。判断一棵二叉树是否对称可以转化为判断它的左子树和右子树是否镜像对称。具体步骤如下:
#include <stdbool.h>
#include <stdio.h>
struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
};
// 辅助函数,判断两棵二叉树是否镜像对称
bool isSymmetricHelper(struct TreeNode* leftNode, struct TreeNode* rightNode) {
// 左子树和右子树都为空,是对称的
if (leftNode == NULL && rightNode == NULL) {
return true;
}
// 左子树和右子树其中一个为空,不对称
if (leftNode == NULL || rightNode == NULL) {
return false;
}
// 当前节点的值相等,并且左子树的左子树和右子树的右子树对称
// 左子树的右子树和右子树的左子树对称时,才是对称的
return (leftNode->val == rightNode->val) &&
isSymmetricHelper(leftNode->left, rightNode->right) &&
isSymmetricHelper(leftNode->right, rightNode->left);
}
// 主函数,判断二叉树是否对称
bool isSymmetric(struct TreeNode* root) {
if (root == NULL) {
return true;
}
return isSymmetricHelper(root->left, root->right);
}
// 测试函数
int main() {
// 构建二叉树
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = 1;
struct TreeNode* node2 = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node2->val = 2;
struct TreeNode* node3 = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node3->val = 2;
struct TreeNode* node4 = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node4->val = 3;
struct TreeNode* node5 = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node5->val = 3;
root->left = node2;
root->right = node3;
node2->left = node4;
node2->
right = NULL;
node3->left = NULL;
node3->right = node5;
node4->left = NULL;
node4->right = NULL;
node5->left = NULL;
node5->right = NULL;
// 判断二叉树是否对称
bool result = isSymmetric(root);
if (result) {
printf("The binary tree is symmetric.\n");
} else {
printf("The binary tree is not symmetric.\n");
}
return 0;
}
上述代码中,我们构建了一个对称的二叉树,并通过判断函数判断了该二叉树是否对称。输出结果表明该二叉树是对称的。
经典面试题2:给定一个无向图,通过深度优先遍历算法遍历图中的所有节点。 解题思路: 深度优先遍历(DFS)是一种递归的遍历算法,它从一个起始节点开始,沿着一条路径尽可能深地遍历图,直到无法继续为止,然后回溯到上一个节点,再选择另一条未访问过的路径继续遍历,直到所有节点都被访问。
#include <stdbool.h>
#include <stdio.h>
#define MAX_VERTICES 100
struct Graph {
int numVertices;
bool adjMatrix[MAX_VERTICES][MAX_VERTICES];
};
void DFS(struct Graph* graph, int vertex, bool visited[]) {
visited[vertex] = true;
printf("Visited vertex: %d\n", vertex);
for (int i = 0; i < graph->numVertices; ++i) {
if (graph->adjMatrix[vertex][i] && !visited[i]) {
DFS(graph, i, visited);
}
}
}
void depthFirstSearch(struct Graph* graph, int startVertex) {
bool visited[MAX_VERTICES] = {false};
DFS(graph, startVertex, visited);
}
// 测试函数
int main() {
struct Graph graph;
int numVertices = 6;
graph.numVertices = numVertices;
// 初始化邻接矩阵
for (int i = 0; i < numVertices; ++i) {
for (int j = 0; j < numVertices; ++j) {
graph.adjMatrix[i][j] = false;
}
}
// 添加边
graph.adjMatrix[0][1] = true;
graph.adjMatrix[0][2] = true;
graph.adjMatrix
[1][3] = true;
graph.adjMatrix[1][4] = true;
graph.adjMatrix[2][5] = true;
int startVertex = 0;
// 执行深度优先遍历
depthFirstSearch(&graph, startVertex);
return 0;
}
上述代码中,我们创建了一个无向图,并使用深度优先遍历算法遍历了该图的所有节点。输出结果按照访问顺序打印了节点编号。
树和图是数据结构中常见且重要的非线性结构。它们在计算机科学和软件开发中具有广泛的应用。以下是对树和图的总结:
通过学习和应用树和图的知识,我们可以更好地解决实际问题,提高算法设计和数据结构应用的能力。