赞
踩
假设我们有一家人,他们的谱系如下图所示:
A
/ \
B C
/ \ / \
D E F G
在这个谱系图中,每个人都代表着一个节点。节点是树的基本单位,它存储了一些数据,并且可能会连接到其他节点。在这里,每个节点代表一个家庭成员,而节点上存储的数据则包括姓名、出生日期等信息。
这些节点之间的连接称为边。边表示一个节点与另一个节点之间的关系。在这个例子中,D和E两个节点是B节点的子节点,因此从B到D和E之间存在两条边。而A节点则连接着B和C两个节点,因此从A到B和C之间也存在两条边。
树的最顶端的节点称为根。在这个例子中,A节点就是整棵树的根节点。根节点是树的起点,所有其他节点都可以通过它来访问。
叶子节点是指没有子节点的节点。在这个例子中,D、E、F和G四个节点就是叶子节点。它们没有任何子节点,只有父节点和兄弟节点。叶子节点也被称为终端节点,因为它们不再连接其他节点。
深度:每个节点都有一个深度值,它表示这个节点到根节点的距离。在这个例子中,A节点的深度为0,B和C节点的深度为1,D、E、F和G节点的深度为2。
高度:树的高度是指从根节点到最远叶子节点的距离。在这个例子中,树的高度为2,因为最远的叶子节点与根节点的距离为2。
子树:每个节点可以看作是一棵子树的根节点。子树包括该节点及其所有后代节点以及它们之间的边。例如,在上面的例子中,B节点的子树包括D和E两个节点以及它们之间的边。
父节点和兄弟节点:每个节点都有一个父节点,除了根节点。在上面的例子中,B和C节点的父节点是A节点。同时,具有相同父节点的节点称为兄弟节点。例如,D和E节点是兄弟节点,它们都是B节点的子节点。
祖先节点和后代节点:节点的祖先节点是指它的父节点、父节点的父节点、父节点的父节点的父节点,以此类推。例如,B节点的祖先节点包括A节点和A节点的父节点(如果有的话)。相反,后代节点是指一个节点的子节点、子节点的子节点、子节点的子节点的子节点,以此类推。例如,B节点的后代节点包括D和E节点。
树是一种非线性数据结构,由若干个节点(vertex)和连接它们的边(edge)组成。树具有以下几个性质:
根据树的特征,我们可以将其分为以下几类:
树还可以按照其形状和性质进行更加具体的划分:
树的遍历算法分为三种:前序遍历、中序遍历和后序遍历。下面通过C语言来举例说明:
假设我们有如下一棵二叉树:
1
/ \
2 3
/ \ / \
4 5 6 7
1、前序遍历(Preorder Traversal)
前序遍历按照“根左右”的顺序访问节点,即先访问根节点,然后递归地访问其左子树,最后递归地访问其右子树。
C代码实现如下:
void preorderTraversal(TreeNode* root){
if(root == NULL) return;
printf("%d ", root->val); // 先访问根节点
preorderTraversal(root->left); // 再递归遍历左子树
preorderTraversal(root->right); // 最后递归遍历右子树
}
输出结果为:1 2 4 5 3 6 7
2、中序遍历(Inorder Traversal)
中序遍历按照“左根右”的顺序访问节点,即先递归地访问左子树,然后访问根节点,最后递归地访问右子树。
C代码实现如下:
void inorderTraversal(TreeNode* root){
if(root == NULL) return;
inorderTraversal(root->left); // 先递归遍历左子树
printf("%d ", root->val); // 再访问根节点
inorderTraversal(root->right); // 最后递归遍历右子树
}
输出结果为:4 2 5 1 6 3 7
3、后序遍历(Postorder Traversal)
后序遍历按照“左右根”的顺序访问节点,即先递归地访问左子树,然后递归地访问右子树,最后访问根节点。
C代码实现如下:
void postorderTraversal(TreeNode* root){
if(root == NULL) return;
postorderTraversal(root->left); // 先递归遍历左子树
postorderTraversal(root->right); // 再递归遍历右子树
printf("%d ", root->val); // 最后访问根节点
}
输出结果为:4 5 2 6 7 3 1
以上就是二叉树的三种遍历算法的C语言实现。需要注意的是,这里的TreeNode结构体表示树的一个节点,其中val表示节点的值,left和right分别表示节点的左子树和右子树。
二叉树是一种特殊的树形数据结构,它由一组节点和一组有向边组成。每个节点包含一个值或数据元素,以及指向左子树和右子树的两个指针,这些指针可以为空。以下是二叉树的定义:
定义:一个二叉树是满足以下条件的树形结构:
二叉树具有以下性质:
二叉树的层次遍历是按照从上到下、从左到右的顺序遍历二叉树中的所有节点。具体实现过程可以通过队列来完成,算法步骤如下:
#include <stdio.h> #include <stdlib.h> // 定义二叉树的节点结构体 typedef struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; } TreeNode; // 定义队列结构体 typedef struct QueueNode { TreeNode *data; struct QueueNode *next; } QueueNode; typedef struct Queue { QueueNode *front, *rear; } Queue; // 初始化队列 void initQueue(Queue *q) { q->front = q->rear = NULL; } // 判断队列是否为空 int isEmpty(Queue *q) { return q->front == NULL; } // 入队列 void enqueue(Queue *q, TreeNode *data) { QueueNode *newNode = (QueueNode *)malloc(sizeof(QueueNode)); newNode->data = data; newNode->next = NULL; if (isEmpty(q)) { q->front = q->rear = newNode; } else { q->rear->next = newNode; q->rear = newNode; } } // 出队列 TreeNode *dequeue(Queue *q) { if (isEmpty(q)) { printf("Queue is empty!\n"); return NULL; } TreeNode *data = q->front->data; QueueNode *temp = q->front; q->front = q->front->next; free(temp); return data; } // 创建二叉树 TreeNode *createTree() { int val; scanf("%d", &val); if (val == -1) { return NULL; } TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode)); root->val = val; root->left = createTree(); root->right = createTree(); return root; } // 层次遍历 void levelOrder(TreeNode *root) { if (root == NULL) { return; } Queue q; initQueue(&q); enqueue(&q, root); while (!isEmpty(&q)) { TreeNode *node = dequeue(&q); printf("%d ", node->val); if (node->left != NULL) { enqueue(&q, node->left); } if (node->right != NULL) { enqueue(&q, node->right); } } } // 主函数 int main() { printf("请输入二叉树的节点,-1表示空节点:\n"); TreeNode *root = createTree(); printf("层次遍历结果为:\n"); levelOrder(root); printf("\n"); return 0; }
平衡二叉树是一种特殊的二叉查找树,具有以下两个性质:
平衡二叉树有多种实现方式,其中 AVL 树和红黑树是最常用的两种:
平衡二叉树具有以下一些重要的性质:
AVL树是一种自平衡二叉搜索树,也被称为高度平衡树。它在每个节点中维护一个平衡因子(balance factor),该因子表示其左子树的高度和右子树的高度之差。AVL树的平衡因子必须为-1、0或1,否则就需要通过旋转来重新平衡。
以下是AVL树的基本实现:
#include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node* left; struct node* right; int height; } Node; int height(Node* n) { if (n == NULL) { return 0; } return n->height; } Node* newNode(int data) { Node* node = (Node*)malloc(sizeof(Node)); node->data = data; node->left = NULL; node->right = NULL; node->height = 1; //新节点高度设为1 return node; } Node* rotate_right(Node* y) { Node* x = y->left; Node* T2 = x->right; x->right = y; y->left = T2; y->height = max(height(y->left), height(y->right)) + 1; x->height = max(height(x->left), height(x->right)) + 1; return x; } Node* rotate_left(Node* x) { Node* y = x->right; Node* T2 = y->left; y->left = x; x->right = T2; x->height = max(height(x->left), height(x->right)) + 1; y->height = max(height(y->left), height(y->right)) + 1; return y; } int getBalance(Node* n) { if (n == NULL) { return 0; } return height(n->left) - height(n->right); } Node* insert(Node* node, int data) { if (node == NULL) { return newNode(data); } if (data < node->data) { node->left = insert(node->left, data); } else if (data > node->data) { node->right = insert(node->right, data); } else { return node; //插入相同元素时,不做操作 } node->height = 1 + max(height(node->left), height(node->right)); int balance = getBalance(node); if (balance > 1 && data < node->left->data) { return rotate_right(node); } if (balance < -1 && data > node->right->data) { return rotate_left(node); } if (balance > 1 && data > node->left->data) { node->left = rotate_left(node->left); return rotate_right(node); } if (balance < -1 && data < node->right->data) { node->right = rotate_right(node->right); return rotate_left(node); } return node; } void preorder(Node* root) { if (root != NULL) { printf("%d ", root->data); preorder(root->left); preorder(root->right); } } int main() { Node* root = NULL; root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); root = insert(root, 50); root = insert(root, 25); printf("Preorder traversal of the constructed AVL tree is:\n"); preorder(root); return 0; }
在这个例子中,小编使用C语言实现了AVL树的基本功能。使用AVL树的一个重要优点是它能够快速地进行查找、插入和删除操作,并且保证树的平衡性。
红黑树是一种自平衡二叉查找树,它可以保证在最坏情况下基本动态集合操作的时间复杂度为O(log n)。红黑树采用颜色标记来维护平衡性,每个节点都被涂上红色或黑色,同时满足以下5个性质:
通过以上性质,我们可以保证红黑树的高度不会超过 2log(n+1),从而保证了查找、插入和删除操作的时间复杂度都为O(log n)。
以下是C语言实现红黑树的结构体和函数:
#include <stdio.h> #include <stdlib.h> typedef struct RBNode{ int data; int color; struct RBNode *left, *right, *parent; }RBNode; typedef struct RBTree{ RBNode *root; RBNode *NIL; }RBTree; void initRBTree(RBTree *T){ T->NIL = (RBNode*)malloc(sizeof(RBNode)); T->NIL->color = 0; T->root = T->NIL; } void LeftRotate(RBTree *T, RBNode *x) { RBNode *y = x->right; x->right = y->left; if(y->left != T->NIL) y->left->parent = x; y->parent = x->parent; if(x->parent == T->NIL) T->root = y; else if(x == x->parent->left) x->parent->left = y; else x->parent->right = y; y->left = x; x->parent = y; } void RightRotate(RBTree *T, RBNode *y) { RBNode *x = y->left; y->left = x->right; if(x->right != T->NIL) x->right->parent = y; x->parent = y->parent; if(y->parent == T->NIL) T->root = x; else if(y == y->parent->left) y->parent->left = x; else y->parent->right = x; x->right = y; y->parent = x; } void RBInsert(RBTree *T, int data) { RBNode *z = (RBNode*)malloc(sizeof(RBNode)); z->data = data; z->left = T->NIL; z->right = T->NIL; z->color = 1; RBNode *y = T->NIL; RBNode *x = T->root; while(x != T->NIL){ y = x; if(z->data < x->data) x = x->left; else x = x->right; } z->parent = y; if(y == T->NIL) T->root = z; else if(z->data < y->data) y->left = z; else y->right = z; while(z->parent->color == 1){ if(z->parent == z->parent->parent->left){ RBNode *y = z->parent->parent->right; if(y->color == 1){ z->parent->color = 0; y->color = 0; z->parent->parent->color = 1; z = z->parent->parent; }else{ if(z == z->parent->right){ z = z->parent; LeftRotate(T, z); } z->parent->color = 0; z->parent->parent->color = 1; RightRotate(T, z->parent->parent); } }else{ RBNode *y = z->parent->parent->left; if(y->color == 1){ z->parent->color = 0; y->color = 0; z->parent->parent->color = 1; z = z->parent->parent; }else{ if(z == z->parent->left){ z = z->parent; RightRotate(T, z); } z->parent->color = 0; z->parent->parent->color = 1; LeftRotate(T, z->parent->parent); } } } T->root->color = 0; } RBNode* RBSearch(RBTree *T, int data) { RBNode *x = T->root; while(x != T->NIL && x->data != data){ if(data < x->data) x = x->left; else x = x->right; } if(x != T->NIL) return x; else return NULL; } void RBDelete(RBTree *T, int data) { RBNode *z = RBSearch(T, data); if(z == NULL) return; RBNode *y, *x; if(z->left == T->NIL || z->right == T->NIL) y = z; else{ y = z->right; while(y->left != T->NIL) y = y->left; } if(y->left != T->NIL) x = y->left; else x = y->right; x->parent = y->parent; if(y->parent == T->NIL) T->root = x; else if(y == y->parent->left) y->parent->left = x; else y->parent->right = x; if(y != z) z->data = y->data; if(y->color == 0){ while(x != T->root && x->color == 0){ if(x == x->parent->left){ RBNode *w = x->parent->right; if(w->color == 1){ w->color = 0; x->parent->color = 1; LeftRotate(T, x->parent); w = x->parent->right; } if(w->left->color == 0 && w->right->color == 0){ w->color = 1; x = x->parent; }else{ if(w->right->color == 0){ w->left->color = 0; w->color = 1; RightRotate(T, w); w = x->parent->right; } w->color = x->parent->color; x->parent->color = 0; w->right->color = 0; LeftRotate(T, x->parent); x = T->root; } }else{ RBNode *w = x->parent->left; if(w->color == 1){ w->color = 0; x->parent->color = 1; RightRotate(T, x->parent); w = x->parent->left; } if(w->right->color == 0 && w->left->color == 0){ w->color = 1; x = x->parent; }else{ if(w->left->color == 0){ w->right->color = 0; w->color = 1; LeftRotate(T, w); w = x->parent->left; } w->color = x->parent->color; x->parent->color = 0; w->left->color = 0; RightRotate(T, x->parent); x = T->root; } } } x->color = 0; } free(y); } void InOrder(RBNode *root) { if(root->left) InOrder(root->left); printf("%d ", root->data); if(root->right) InOrder(root->right); } int main() { RBTree T; initRBTree(&T); int arr[] = {11,2,14,1,7,15,5,8,4}; for(int i=0;i<9;i++){ RBInsert(&T, arr[i]); } printf("Inorder traversal of the constructed RB tree: "); InOrder(T.root); printf("\n"); RBDelete(&T, 11); RBDelete(&T, 14); printf("Inorder traversal of the modified RB tree: "); InOrder(T.root); printf("\n"); return 0; }
这段代码实现了红黑树的插入、删除和查找操作,并提供了一个中序遍历函数InOrder
用于打印树形结构。在主函数中,我们初始化了一个红黑树并插入了一些元素,然后删除了两个节点,并打印出修改后的树形结构。
B树是一种多路平衡查找树,它的定义和特点如下:
定义:B树是一棵根节点至少有两个子节点的树,每个节点可以存放多个关键字,并按照大小顺序从小到大排列,同时每个节点的子节点数目也与关键字数目相同或者比关键字数目多1。
特点:
B+树是B树的一种变体,它与B树的区别在于:① 所有数据都存储在叶子节点上;② 叶子节点之间采用指针相连接,形成一个有序链表,便于范围查询。
下面是一个简单的C语言实现B+树的例子:
#include <stdio.h> #include <stdlib.h> typedef struct Node { int val; struct Node* next; } Node; typedef struct BPlusTree { int degree; Node* root; } BPlusTree; // 创建新节点 Node* createNode(int val) { Node* node = (Node*) malloc(sizeof(Node)); node->val = val; node->next = NULL; return node; } // 向有序链表中插入元素 void insertToList(Node** list, int val) { Node* node = createNode(val); if (*list == NULL || val < (*list)->val) { node->next = *list; *list = node; } else { Node* cur = *list; while (cur->next != NULL && val >= cur->next->val) { cur = cur->next; } node->next = cur->next; cur->next = node; } } // 初始化B+树 BPlusTree* initBPlusTree(int degree) { BPlusTree* tree = (BPlusTree*) malloc(sizeof(BPlusTree)); tree->degree = degree; tree->root = NULL; return tree; } // 插入元素 void insertBPlusTree(BPlusTree* tree, int val) { if (tree->root == NULL) { tree->root = createNode(val); } else { Node* cur = tree->root; Node* parent = NULL; while (cur != NULL && cur->val <= val) { parent = cur; cur = cur->next; } if (parent == NULL) { insertToList(&(tree->root), val); } else { insertToList(&(parent->next), val); } } } // 输出有序链表中的元素 void printList(Node* list) { while (list != NULL) { printf("%d ", list->val); list = list->next; } } // 遍历B+树 void traverseBPlusTree(BPlusTree* tree) { Node* cur = tree->root; while (cur != NULL) { printList(cur); cur = cur->next; } } int main() { BPlusTree* tree = initBPlusTree(3); insertBPlusTree(tree, 1); insertBPlusTree(tree, 5); insertBPlusTree(tree, 2); insertBPlusTree(tree, 7); insertBPlusTree(tree, 3); insertBPlusTree(tree, 4); insertBPlusTree(tree, 6); traverseBPlusTree(tree); // 输出 1 2 3 4 5 6 7 return 0; }
上面的代码演示了如何用C语言实现一个简单的B+树,其中节点为链表,元素按从小到大的顺序排列。具体来说,主要实现了以下几个函数:
B*树是B+树的一种优化,它与B+树的区别在于:① 在非叶子节点中存储更多的关键字,使得非叶子节点可以容纳更多的子节点;② 叶子节点和非叶子节点的大小差距更小。
下面是一个简单的C语言实现B*树的例子:
#include <stdio.h> #include <stdlib.h> #define DEGREE 3 typedef struct Node { int num; // 关键字数量 int vals[2 * DEGREE + 1]; // 关键字数组 struct Node* children[2 * DEGREE + 2]; // 子节点数组 } Node; // 创建新节点 Node* createNode() { Node* node = (Node*) malloc(sizeof(Node)); node->num = 0; for (int i = 0; i < 2 * DEGREE + 2; i++) { node->children[i] = NULL; } return node; } // 在节点中查找关键字位置 int findIndex(Node* node, int val) { int i = 0; while (i < node->num && node->vals[i] < val) { i++; } return i; } // 插入关键字到节点中 void insertIntoNode(Node* node, int val) { int i = node->num - 1; while (i >= 0 && node->vals[i] > val) { node->vals[i + 1] = node->vals[i]; i--; } node->vals[i + 1] = val; node->num++; } // 分裂节点 void splitNode(Node* parent, int index) { Node* node = parent->children[index]; Node* newNode = createNode(); int mid = DEGREE; for (int i = mid + 1; i < 2 * DEGREE + 1; i++) { newNode->vals[i - mid - 1] = node->vals[i]; newNode->children[i - mid - 1] = node->children[i]; if (newNode->children[i - mid - 1] != NULL) { newNode->children[i - mid - 1]->parent = newNode; } } newNode->children[DEGREE] = node->children[2 * DEGREE + 1]; if (newNode->children[DEGREE] != NULL) { newNode->children[DEGREE]->parent = newNode; } newNode->num = DEGREE; for (int i = mid; i < 2 * DEGREE; i++) { node->vals[i] = 0; node->children[i + 1] = NULL; } node->num = DEGREE; insertIntoNode(parent, node->vals[mid]); int j = parent->num - 1; while (j > index) { parent->children[j + 1] = parent->children[j]; j--; } parent->children[index + 1] = newNode; newNode->parent = parent; } // 插入关键字到B*树中 void insertBStarTree(Node** root, Node* node, int val) { int index = findIndex(node, val); if (node->children[index] != NULL) { insertBStarTree(root, node->children[index], val); if (node->children[index]->num > 2 * DEGREE) { splitNode(node, index); } } else { insertIntoNode(node, val); if (node->num > 2 * DEGREE && node != *root) { Node* parent = node->parent; int childIndex = findIndex(parent, node->vals[DEGREE]); splitNode(parent, childIndex); } } } // 输出B*树中的关键字 void printBStarTree(Node* root) { if (root != NULL) { for (int i = 0; i < root->num; i++) { printf("%d ", root->vals[i]); } printf("\n"); for (int i = 0; i <= root->num; i++) { printBStarTree(root->children[i]); } } } int main() { Node* root = createNode(); insertBStarTree(&root, root, 1); insertBStarTree(&root, root, 5); insertBStarTree(&root, root, 2); insertBStarTree(&root, root, 7); insertBStarTree(&root, root, 3); insertBStarTree(&root, root, 4); insertBStarTree(&root, root, 6); printBStarTree(root); // 输出 1 2 3 4 5 6 7 return 0; }
上面的代码演示了如何用C语言实现一个简单的B*树,其中节点中存储多个关键字,非叶子节点也可以容纳更多的子节点。具体来说,主要实现了以下几个函数:
哈夫曼编码是一种可变字长编码方式,可以将不同长度的字符编码成等长的比特串,并且保证解码时不会出现歧义。哈夫曼编码的核心思想是利用字符出现的频率来构造一棵小根堆(也称为哈夫曼树),并且将频率较低的字符放在叶子节点的左侧,频率较高的字符放在叶子节点的右侧,从而使得编码后的字符串具有最短的平均长度。
下面是一个简单的C语言实现哈夫曼编码的例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_SIZE 512 // 哈夫曼树节点 typedef struct HuffmanNode { char ch; int freq; struct HuffmanNode* left; struct HuffmanNode* right; } HuffmanNode; // 小根堆节点 typedef struct MinHeapNode { HuffmanNode* node; struct MinHeapNode* next; } MinHeapNode; // 创建新的哈夫曼树节点 HuffmanNode* createHuffmanNode(char ch, int freq) { HuffmanNode* node = (HuffmanNode*) malloc(sizeof(HuffmanNode)); node->ch = ch; node->freq = freq; node->left = NULL; node->right = NULL; return node; } // 创建新的小根堆节点 MinHeapNode* createMinHeapNode(HuffmanNode* node) { MinHeapNode* heapNode = (MinHeapNode*) malloc(sizeof(MinHeapNode)); heapNode->node = node; heapNode->next = NULL; return heapNode; } // 插入小根堆节点 void insertMinHeapNode(MinHeapNode** head, HuffmanNode* node) { MinHeapNode* heapNode = createMinHeapNode(node); if (*head == NULL || (*head)->node->freq > node->freq) { heapNode->next = *head; *head = heapNode; } else { MinHeapNode* cur = *head; while (cur->next != NULL && cur->next->node->freq <= node->freq) { cur = cur->next; } heapNode->next = cur->next; cur->next = heapNode; } } // 删除小根堆头节点 HuffmanNode* removeMinHeapNode(MinHeapNode** head) { if (*head == NULL) { return NULL; } else { HuffmanNode* node = (*head)->node; MinHeapNode* tmp = *head; *head = (*head)->next; free(tmp); return node; } } // 构建哈夫曼树 HuffmanNode* buildHuffmanTree(char* str, int* freqs) { MinHeapNode* heapHead = NULL; // 统计字符频率并插入小根堆 for (int i = 0; i < strlen(str); i++) { char ch = str[i]; HuffmanNode* node = createHuffmanNode(ch, freqs[ch]); insertMinHeapNode(&heapHead, node); } // 合并小根堆中的节点,构造哈夫曼树 while (heapHead->next != NULL) { HuffmanNode* node1 = removeMinHeapNode(&heapHead); HuffmanNode* node2 = removeMinHeapNode(&heapHead); HuffmanNode* parent = createHuffmanNode('\0', node1->freq + node2->freq); parent->left = node1; parent->right = node2; insertMinHeapNode(&heapHead, parent); } return removeMinHeapNode(&heapHead); } // 生成哈夫曼编码表 void generateHuffmanTable(HuffmanNode* root, char** table, char* code) { if (root != NULL) { if (root->ch != '\0') { int index = (int) root->ch; table[index] = (char*) malloc(strlen(code) + 1); strcpy(table[index], code); } char leftCode[MAX_SIZE], rightCode[MAX_SIZE]; strcpy(leftCode, code); strcat(leftCode, "0"); strcat(rightCode, "1"); generateHuffmanTable(root->left, table, leftCode); generateHuffmanTable(root->right, table, rightCode); } } // 使用哈夫曼编码表对字符串进行编码 char* encodeString(char* str, char** table) { int len = strlen(str); char* code = (char*) malloc(MAX_SIZE * len * sizeof(char)); strcpy(code, ""); for (int i = 0; i < len; i++) { int index = (int) str[i]; strcat(code, table[index]); } return code; } // 使用哈夫曼编码表对比特串进行解码 char* decodeString(char* code, HuffmanNode* root) { int len = strlen(code); char* str = (char*) malloc(MAX_SIZE * len * sizeof(char)); strcpy(str, ""); HuffmanNode* cur = root; for (int i = 0; i < len; i++) { if (code[i] == '0') { cur = cur->left; } else if (code[i] == '1') { cur = cur->right; } if (cur->ch != '\0') { str[strlen(str)] = cur->ch; cur = root; } } return str; } int main() { char str[MAX_SIZE] = "hello world"; int freqs[128] = {0}; // 统计字符频率 for (int i = 0; i < strlen(str); i++) { char ch = str[i]; freqs[ch]++; } // 构建哈夫曼树并生成哈夫曼编码表 HuffmanNode* root = buildHuffmanTree(str, freqs); char* table[128] = {0}; generateHuffmanTable(root, table, ""); // 打印哈夫曼编码表 for (int i = 0; i < 128; i++) { if (table[i] != NULL) { printf("'%c': %s\n", (char) i, table[i]); } } // 对字符串进行编码和解码 char* code = encodeString(str, table); printf("Encoded: %s\n", code); char* decodedStr = decodeString(code, root); printf("Decoded: %s\n", decodedStr); return 0; }
上面的代码演示了如何用C语言实现一个简单的哈夫曼编码器,其中主要实现了以下几个函数:
并查集是一种用于维护一些不相交集合的数据结构,支持合并(即将两个集合合并成一个)和查询(判断两个元素是否属于同一个集合)操作。在实际应用中,常常用并查集来处理连通性问题,比如判断一个图是否连通、最小生成树等。其中,每个元素被视为一个节点,并按照某种规则组织在一起形成一个森林,每个集合对应着一棵树。
下面是一个简单的C语言实现并查集的例子:
#include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 // 并查集节点 typedef struct UnionFindNode { int parent; int rank; } UnionFindNode; // 初始化并查集 void initUnionFind(UnionFindNode sets[]) { for (int i = 0; i < MAX_SIZE; i++) { sets[i].parent = i; sets[i].rank = 0; } } // 查找节点所在的集合(使用路径压缩优化) int findSet(UnionFindNode sets[], int x) { if (sets[x].parent != x) { sets[x].parent = findSet(sets, sets[x].parent); } return sets[x].parent; } // 合并两个集合(使用按秩合并优化) void unionSets(UnionFindNode sets[], int x, int y) { int root1 = findSet(sets, x); int root2 = findSet(sets, y); if (root1 == root2) { return; } if (sets[root1].rank > sets[root2].rank) { sets[root2].parent = root1; } else if (sets[root1].rank < sets[root2].rank) { sets[root1].parent = root2; } else { sets[root2].parent = root1; sets[root1].rank++; } } int main() { UnionFindNode sets[MAX_SIZE]; initUnionFind(sets); // 合并集合 unionSets(sets, 0, 1); unionSets(sets, 1, 2); unionSets(sets, 3, 4); unionSets(sets, 5, 6); unionSets(sets, 6, 7); unionSets(sets, 7, 8); // 查询元素所在的集合 int x = 1, y = 3; printf("%d and %d are%s in the same set.\n", x, y, findSet(sets, x) == findSet(sets, y) ? "" : " not"); return 0; }
上面的代码演示了如何用C语言实现一个简单的并查集,其中主要实现了以下几个函数:
在上述代码中,我们将每个元素视为一个节点,并按照编号的大小组织在一起形成一个森林。初始时每个节点都是自己的父节点,rank表示该集合所在的树的深度(即高度)。通过调用findSet()函数来查找某个节点所在的集合,并且在查找时使用路径压缩优化,可以使得查找的复杂度接近于常数级别。同时,在unionSets()函数中,我们使用按秩合并优化来避免出现不平衡的树,从而提高效率。
并查集常常被应用于图算法、计算机网络等领域,例如用于判断一个无向图是否连通、寻找最小生成树等。
最小生成树算法是指在一个加权连通图中找到一棵生成树,使得树的所有边的权值之和最小。其中,普遍采用的算法有Kruskal算法、Prim算法等。下面我们以Kruskal算法为例,介绍如何使用C语言实现最小生成树算法。
Kruskal算法的核心思想是将边按照权重从小到大排序,并逐个加入到生成树中。如果加入某条边后形成了环,则不加入该边,直到生成树包含了所有的节点。可以使用并查集来判断两个节点是否属于同一个集合(即是否已经连接)。以下是一个简单的C语言实现:
#include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 // 边的结构体 typedef struct Edge { int u, v; // 边的两个端点 int weight; // 边的权重 } Edge; // 并查集节点 typedef struct UnionFindNode { int parent; int rank; } UnionFindNode; // 初始化并查集 void initUnionFind(UnionFindNode sets[]) { for (int i = 0; i < MAX_SIZE; i++) { sets[i].parent = i; sets[i].rank = 0; } } // 查找节点所在的集合(使用路径压缩优化) int findSet(UnionFindNode sets[], int x) { if (sets[x].parent != x) { sets[x].parent = findSet(sets, sets[x].parent); } return sets[x].parent; } // 合并两个集合(使用按秩合并优化) void unionSets(UnionFindNode sets[], int x, int y) { int root1 = findSet(sets, x); int root2 = findSet(sets, y); if (root1 == root2) { return; } if (sets[root1].rank > sets[root2].rank) { sets[root2].parent = root1; } else if (sets[root1].rank < sets[root2].rank) { sets[root1].parent = root2; } else { sets[root2].parent = root1; sets[root1].rank++; } } // 按边权重从小到大排序 int cmp(const void* a, const void* b) { Edge* edgeA = (Edge*) a; Edge* edgeB = (Edge*) b; return edgeA->weight - edgeB->weight; } // 输出最小生成树的边 void printMST(Edge mst[], int n) { printf("Minimum Spanning Tree:\n"); for (int i = 0; i < n - 1; i++) { printf("%d -- %d\tweight: %d\n", mst[i].u, mst[i].v, mst[i].weight); } } // 计算最小生成树 void kruskalMST(Edge edges[], int n, int m) { qsort(edges, m, sizeof(Edge), cmp); // 按边权重从小到大排序 UnionFindNode sets[MAX_SIZE]; initUnionFind(sets); // 初始化并查集 Edge mst[MAX_SIZE] = {0}; // 存储最小生成树的边 int count = 0; // 记录加入的边数 for (int i = 0; i < m && count < n - 1; i++) { int u = edges[i].u, v = edges[i].v; if (findSet(sets, u) != findSet(sets, v)) { // 判断是否形成环 mst[count++] = edges[i]; // 加入到最小生成树中 unionSets(sets, u, v); // 合并两个集合 } } printMST(mst, n); } int main() { int n = 6, m = 9; Edge edges[] = {{0, 1, 6}, {0, 3, 4}, {0, 2, 1}, {1, 2, 5}, {1, 4, 3}, {1, 5, 7}, {2, 4, 8}, {3, 4, 9}, {4, 5, 2}}; kruskalMST(edges, n, m); return 0; }
在上述代码中,我们首先定义了一个Edge
结构体,用于表示边的信息。然后,通过使用并查集来判断两个节点是否属于同一个集合,并按照边权重从小到大排序,依次将边加入最小生成树中,如果形成环则不加入,并记录加入的边数。最后,输出最小生成树的所有边。
这里我们使用了并查集来帮助维护连通性信息,同时使用快速排序算法对所有边进行排序,以便能够高效地加入边并避免形成环。如果边的数量比较大,那么可以考虑使用更加高效的排序算法来排序。
最小生成树算法常见于网络设计、城市道路建设等领域,例如为一张电信网络设计出一个连接所有城市的最小成本方案。
关于树的相关知识,先写到这,进阶的知识点,小编后期会持续更新,希望各位大佬能够点赞支持。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。