二叉树
什么是二叉树?
树是一种非线性的数据结构,它由节点(Node)和边(Edge)组成,并且具有层级关系。每个节点可以有零个或多个子节点。
而二叉树(Binary Tree) 是一种特殊的树,它的每个节点最多只有两个子节点,分别称为左子节点(Left Child) 和 右子节点(Right Child)。
基本概念:
- 根节点(Root Node): 树的顶端节点,没有父节点。
- 父节点(Parent Node): 拥有子节点的节点。
- 子节点(Child Node): 依附于父节点的节点。
- 兄弟节点(Sibling Node): 拥有同一个父节点的节点。
- 叶子节点(Leaf Node): 没有子节点的节点。
- 路径(Path): 从一个节点到另一个节点所经过的节点序列。
- 深度(Depth): 从根节点到某个节点的路径上的边数。根节点的深度为 0。
- 高度(Height): 从某个节点到其最远叶子节点的路径上的边数。叶子节点的高度为 0。树的高度是根节点的高度。
- 层(Level): 节点的深度加 1。根节点在第 1 层。
2. C 语言中的二叉树节点定义
与链表类似,二叉树的节点也可以通过结构体来定义。
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <stdlib.h>
typedef struct TreeNode { int data; struct TreeNode *left; struct TreeNode *right; } TreeNode;
|
3. 二叉树的类型
二叉树有很多种类型,理解它们有助于我们更好地应用:
满二叉树(Full Binary Tree): 除了叶子节点外,所有节点都有两个子节点。
完全二叉树(Complete Binary Tree): 除了最后一层,其他层都被完全填满,并且最后一层的所有节点都尽可能地靠左排列。
平衡二叉树(Balanced Binary Tree): 左右子树的高度差不超过 1,并且左右子树都是平衡二叉树。常见的有 AVL 树和红黑树。
二叉搜索树(Binary Search Tree, BST):
对所有节点都满足以下条件:
- 左子树中所有节点的值都小于当前节点的值。
- 右子树中所有节点的值都大于当前节点的值。
- 左右子树也都是二叉搜索树。
4. 二叉树的遍历方式
遍历二叉树是指按照某种顺序访问树中的所有节点。二叉树的遍历是面试常考点,也是理解树结构的关键。主要有四种基本遍历方式:
4.1 深度优先遍历(Depth-First Search, DFS)
深度优先遍历沿着树的深度方向进行,直到无法继续深入,然后回溯。主要有三种:
前序遍历(Pre-order Traversal): 访问顺序为:根节点 -> 左子树 -> 右子树。
1 2 3 4 5 6 7 8 9
| void preOrderTraversal(TreeNode* root) { if (root == NULL) { return; } printf("%d ", root->data); preOrderTraversal(root->left); preOrderTraversal(root->right); }
|
中序遍历(In-order Traversal): 访问顺序为:左子树 -> 根节点 -> 右子树。
对于二叉搜索树,中序遍历会得到一个有序的序列。
1 2 3 4 5 6 7 8 9
| void inOrderTraversal(TreeNode* root) { if (root == NULL) { return; } inOrderTraversal(root->left); printf("%d ", root->data); inOrderTraversal(root->right); }
|
后序遍历(Post-order Traversal): 访问顺序为:左子树 -> 右子树 -> 根节点。
通常用于在删除节点前先删除子节点,或计算表达式树。
1 2 3 4 5 6 7 8 9
| void postOrderTraversal(TreeNode* root) { if (root == NULL) { return; } postOrderTraversal(root->left); postOrderTraversal(root->right); printf("%d ", root->data); }
|
4.2 广度优先遍历(Breadth-First Search, BFS)/ 层序遍历(Level-order Traversal)
广度优先遍历逐层访问树的节点,从上到下,从左到右。通常需要借助队列(Queue)来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| typedef struct QueueNode { TreeNode* treeNode; struct QueueNode* next; } QueueNode;
typedef struct Queue { QueueNode* front; QueueNode* rear; } Queue;
Queue* createQueue() { Queue* q = (Queue*)malloc(sizeof(Queue)); q->front = NULL; q->rear = NULL; return q; }
void enqueue(Queue* q, TreeNode* treeNode) { QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode)); newNode->treeNode = treeNode; newNode->next = NULL; if (q->rear == NULL) { q->front = newNode; q->rear = newNode; } else { q->rear->next = newNode; q->rear = newNode; } }
TreeNode* dequeue(Queue* q) { if (q->front == NULL) { return NULL; } QueueNode* temp = q->front; TreeNode* treeNode = temp->treeNode; q->front = q->front->next; if (q->front == NULL) { q->rear = NULL; } free(temp); return treeNode; }
int isQueueEmpty(Queue* q) { return q->front == NULL; }
void levelOrderTraversal(TreeNode* root) { if (root == NULL) { return; }
Queue* q = createQueue(); enqueue(q, root);
printf("层序遍历:"); while (!isQueueEmpty(q)) { TreeNode* current = dequeue(q); printf("%d ", current->data);
if (current->left != NULL) { enqueue(q, current->left); } if (current->right != NULL) { enqueue(q, current->right); } } printf("\n"); free(q); }
|
5. 二叉搜索树(BST)的实现
BST 是一种非常实用的二叉树,它能高效地进行查找、插入和删除操作。
5.1 插入节点
插入新节点时,从根节点开始比较,如果新节点的值小于当前节点,则进入左子树;如果大于当前节点,则进入右子树。直到找到一个空位置插入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| TreeNode* insertNode(TreeNode* root, int value) { if (root == NULL) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); if (newNode == NULL) { printf("内存分配失败!\n"); exit(1); } newNode->data = value; newNode->left = NULL; newNode->right = NULL; return newNode; }
if (value < root->data) { root->left = insertNode(root->left, value); } else if (value > root->data) { root->right = insertNode(root->right, value); } return root; }
|
5.2 查找节点
查找节点与插入类似,通过比较值来决定向左还是向右查找。
1 2 3 4 5 6 7 8 9 10 11
| TreeNode* searchNode(TreeNode* root, int value) { if (root == NULL || root->data == value) { return root; } if (value < root->data) { return searchNode(root->left, value); } else { return searchNode(root->right, value); } }
|
5.3 删除节点
删除节点是 BST 操作中最复杂的一个,需要处理以下几种情况:
- 待删除节点是叶子节点: 直接删除,并将其父节点对应的指针置为
NULL
。
- 待删除节点只有一个子节点: 用其子节点替换待删除节点。
- 待删除节点有两个子节点: 找到其右子树中的最小节点(或左子树中的最大节点)来替换它,然后删除那个最小(或最大)节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| TreeNode* findMinNode(TreeNode* node) { TreeNode* current = node; while (current && current->left != NULL) { current = current->left; } return current; }
TreeNode* deleteNode(TreeNode* root, int value) { if (root == NULL) { return root; }
if (value < root->data) { root->left = deleteNode(root->left, value); } else if (value > root->data) { root->right = deleteNode(root->right, value); } else { if (root->left == NULL) { TreeNode* temp = root->right; free(root); return temp; } else if (root->right == NULL) { TreeNode* temp = root->left; free(root); return temp; }
TreeNode* temp = findMinNode(root->right); root->data = temp->data; root->right = deleteNode(root->right, temp->data); } return root; }
|
5.4 释放二叉树内存
递归地释放所有节点。
1 2 3 4 5 6 7 8 9
| void freeTree(TreeNode* root) { if (root == NULL) { return; } freeTree(root->left); freeTree(root->right); free(root); }
|
6. 完整示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
| #include <stdio.h> #include <stdlib.h>
typedef struct TreeNode { int data; struct TreeNode *left; struct TreeNode *right; } TreeNode;
typedef struct QueueNode { TreeNode* treeNode; struct QueueNode* next; } QueueNode;
typedef struct Queue { QueueNode* front; QueueNode* rear; } Queue;
Queue* createQueue() { Queue* q = (Queue*)malloc(sizeof(Queue)); q->front = NULL; q->rear = NULL; return q; }
void enqueue(Queue* q, TreeNode* treeNode) { QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode)); newNode->treeNode = treeNode; newNode->next = NULL; if (q->rear == NULL) { q->front = newNode; q->rear = newNode; } else { q->rear->next = newNode; q->rear = newNode; } }
TreeNode* dequeue(Queue* q) { if (q->front == NULL) { return NULL; } QueueNode* temp = q->front; TreeNode* treeNode = temp->treeNode; q->front = q->front->next; if (q->front == NULL) { q->rear = NULL; } free(temp); return treeNode; }
int isQueueEmpty(Queue* q) { return q->front == NULL; }
void freeQueue(Queue* q) { while (!isQueueEmpty(q)) { dequeue(q); } free(q); }
TreeNode* createNewNode(int value) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); if (newNode == NULL) { printf("内存分配失败!\n"); exit(1); } newNode->data = value; newNode->left = NULL; newNode->right = NULL; return newNode; }
void preOrderTraversal(TreeNode* root) { if (root == NULL) return; printf("%d ", root->data); preOrderTraversal(root->left); preOrderTraversal(root->right); }
void inOrderTraversal(TreeNode* root) { if (root == NULL) return; inOrderTraversal(root->left); printf("%d ", root->data); inOrderTraversal(root->right); }
void postOrderTraversal(TreeNode* root) { if (root == NULL) return; postOrderTraversal(root->left); postOrderTraversal(root->right); printf("%d ", root->data); }
void levelOrderTraversal(TreeNode* root) { if (root == NULL) return; Queue* q = createQueue(); enqueue(q, root); printf("层序遍历:"); while (!isQueueEmpty(q)) { TreeNode* current = dequeue(q); printf("%d ", current->data); if (current->left != NULL) enqueue(q, current->left); if (current->right != NULL) enqueue(q, current->right); } printf("\n"); free(q); }
TreeNode* insertNode(TreeNode* root, int value) { if (root == NULL) { return createNewNode(value); } if (value < root->data) { root->left = insertNode(root->left, value); } else if (value > root->data) { root->right = insertNode(root->right, value); } return root; }
TreeNode* searchNode(TreeNode* root, int value) { if (root == NULL || root->data == value) { return root; } if (value < root->data) { return searchNode(root->left, value); } else { return searchNode(root->right, value); } }
TreeNode* findMinNode(TreeNode* node) { TreeNode* current = node; while (current && current->left != NULL) { current = current->left; } return current; }
TreeNode* deleteNode(TreeNode* root, int value) { if (root == NULL) { return root; }
if (value < root->data) { root->left = deleteNode(root->left, value); } else if (value > root->data) { root->right = deleteNode(root->right, value); } else { if (root->left == NULL) { TreeNode* temp = root->right; free(root); return temp; } else if (root->right == NULL) { TreeNode* temp = root->left; free(root); return temp; } TreeNode* temp = findMinNode(root->right); root->data = temp->data; root->right = deleteNode(root->right, temp->data); } return root; }
void freeTree(TreeNode* root) { if (root == NULL) { return; } freeTree(root->left); freeTree(root->right); free(root); }
int main() { TreeNode* root = NULL;
printf("------ 插入节点 ------\n"); root = insertNode(root, 50); root = insertNode(root, 30); root = insertNode(root, 70); root = insertNode(root, 20); root = insertNode(root, 40); root = insertNode(root, 60); root = insertNode(root, 80);
printf("\n------ 遍历操作 ------\n"); printf("前序遍历:"); preOrderTraversal(root); printf("\n");
printf("中序遍历:"); inOrderTraversal(root); printf("\n");
printf("后序遍历:"); postOrderTraversal(root); printf("\n");
levelOrderTraversal(root);
printf("\n------ 查找操作 ------\n"); if (searchNode(root, 40) != NULL) { printf("找到了值为 40 的节点。\n"); } else { printf("未找到值为 40 的节点。\n"); }
if (searchNode(root, 90) != NULL) { printf("找到了值为 90 的节点。\n"); } else { printf("未找到值为 90 的节点。\n"); }
printf("\n------ 删除操作 ------\n"); printf("删除节点 20 (叶子节点):\n"); root = deleteNode(root, 20); levelOrderTraversal(root);
printf("删除节点 70 (有一个子节点):\n"); root = deleteNode(root, 70); levelOrderTraversal(root);
printf("删除节点 50 (有两个子节点):\n"); root = deleteNode(root, 50); levelOrderTraversal(root);
printf("\n------ 内存释放 ------\n"); freeTree(root); printf("二叉树内存已释放。\n");
return 0; }
|