数据结构从入门到实战——二叉树
目录
前言
一、二叉树链式结构的实现
1.1 二叉树的结构以及方法声明
1.2 手搓一个简单的二叉树(创建二叉树后面会详细讲解)
1.3 前序遍历
1.4 中序遍历
1.5 后续遍历
1.6 计算二叉树的节点个数
1.7 计算二叉树叶子节点的个数
1.8 计算二叉树的高度
1.9 二叉树的销毁
1.10 层序遍历(需要用到队列)
1.11 判断二叉树是否为完全二叉树
码云链接:https://gitee.com/a-qian-c-language/data-structure.git
前言
在计算机科学的演进历程中,数据结构作为组织与管理信息的核心工具,始终扮演着至关重要的角色。面对日益复杂的数据处理需求,线性结构在表达层级关系与实现高效操作方面逐渐显现出局限性。为此,树形结构应运而生,以其天然的层次化特性,为解决分治、搜索、排序与表达式处理等复杂计算问题提供了强有力的模型支撑。
二叉树的基本功能:
- 层次化组织结构:二叉树以唯一的根节点(Root)为逻辑起点,通过指针或引用建立明确的父子层级关系,自顶向下逐层扩展。每个节点最多拥有两个子节点——左子节点与右子节点,形成严格的二分分支结构。这种天然的分层特性使其能够高效地模拟现实世界中的层级关系(如组织架构、目录系统),并为分治算法(Divide and Conquer)提供理想的实现基础。
- 高效的遍历机制:二叉树支持多种系统化的节点访问策略,主要包括前序遍历(根 → 左子树 → 右子树)、中序遍历(左子树 → 根 → 右子树)、后序遍历(左子树 → 右子树 → 根)和层序遍历(按深度逐层从左至右)。
- 动态插入与删除:在二叉树的有序变体(如二叉搜索树,BST)中,元素的插入与删除操作可在维持结构有序性的前提下高效完成。通过比较节点值决定插入/删除路径,平均时间复杂度可达 O(log n),显著优于线性结构的 O(n)。这一特性使其成为动态数据集合的理想选择,广泛应用于需要频繁增删查改的场景。
- 天然支持递归操作:二叉树具有高度的自相似性——任一子树本身仍是一棵二叉树。这一特性使其与递归算法完美契合。无论是查找、插入、删除、遍历还是求树高、节点数等操作,均可通过简洁的递归函数实现,代码逻辑清晰,易于理解与维护,充分体现了“分而治之”的计算思想。
- 多用途衍生结构:作为基础数据结构,二叉树是众多高级结构的构建基石。通过施加特定约束或优化策略,可衍生出平衡二叉搜索树(如AVL树、红黑树,保证 O(log n) 最坏性能)、堆(Heap,实现优先队列)、哈夫曼树(Huffman Tree,用于数据压缩)以及线段树、Trie树等。这些衍生结构在数据库索引、操作系统调度、文件压缩、字符串匹配等关键领域发挥着不可替代的作用。
本文旨在系统阐述二叉树的基本功能与核心特性,深入剖析其在数据组织、遍历策略、动态操作及递归处理等方面的独特优势,并揭示其作为多种高级数据结构原型的重要地位。通过本篇内容,读者将能够全面理解二叉树的设计哲学与应用价值,为进一步探索算法与系统设计的深层原理奠定坚实基础。
正文开始:
一、二叉树链式结构的实现
项目结构如下:
1.1 二叉树的结构以及方法声明
BT.c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//二叉树的结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;//前序遍历
void PrevOrder(BTNode* root);
//中序遍历
void InOrder(BTNode* root);
//后续遍历
void BackOrder(BTNode* root);//二叉树节点的个数
int TreeSize(BTNode* root);//叶子节点的个数
int TreeLeafSize(BTNode* root);//二叉树的高度
int TreeHeight(BTNode* root);//二叉树第K层的节点数
int TreeLevalSize(BTNode* root);//查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x);//二叉树的销毁
void BinaryTreeDestroy(BTNode* root);//二叉树的层序遍历
void TreeLevalOrder(BTNode* root);//判断一个二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root);
1.2 手搓一个简单的二叉树(创建二叉树后面会详细讲解)
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "BT.h"BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail!");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}
//手搓一个二叉树
BTNode* CreateTree()
{//创建节点BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}int main()
{//手搓一个二叉树BTNode* root = CreateTree();return 0;
}
1.3 前序遍历
BT.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "BT.h"//前序遍历
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}
测试结果如下:
1.4 中序遍历
//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}PrevOrder(root->left);printf("%d ", root->data);PrevOrder(root->right);
}
测试结果如下:
1.5 后续遍历
//后续遍历
void BackOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BackOrder(root->left);BackOrder(root->right);printf("%d ", root->data);}
测试结果如下:
1.6 计算二叉树的节点个数
//二叉树节点的个数
int TreeSize(BTNode* root)
{if (root == NULL){return 0;}return TreeSize(root->left) + TreeSize(root->right) + 1;
}
1.7 计算二叉树叶子节点的个数
//叶子节点的个数
int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
1.8 计算二叉树的高度
//二叉树的高度
int TreeHeight(BTNode* root)
{if (root == NULL){return 0;}int left = TreeHeight(root->left);int right = TreeHeight(root->right);return left > right ? left + 1 : right + 1;
}
1.9 二叉树的销毁
//二叉树的销毁
void BinaryTreeDestroy(BTNode* root)
{//从最小面开始销毁,所以使用后续if (root == NULL){return;}BinaryTreeDestroy(root->left);BinaryTreeDestroy(root->right);free(root);
}
1.10 层序遍历(需要用到队列)
Q.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//定义队列的结构
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{struct QueueNode* next;QDataType val;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Que;//初始化
void QueueInit(Que* pq);
//销毁
void QueueDestroy(Que* pq);//队尾插入
void QueuePush(Que* pq, QDataType x);
//对头删除
void QueuePop(Que* pq);//取队头的数据
QDataType QueueFront(Que* pq);
//取队尾的数据
QDataType QueueBack(Que* pq);//判断队列是否为空
bool QueueEmpty(Que* pq);
//取队列节点的个数
int QueueSize(Que* pq);
Q.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Q.h"//初始化
void QueueInit(Que* pq)
{assert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}//销毁
void QueueDestroy(Que* pq)
{assert(pq);QNode* pcur = pq->phead;while (pcur){QNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//队尾插入
void QueuePush(Que* pq, QDataType x)
{assert(pq);//创建节点QNode* node = (QNode*)malloc(sizeof(QNode));if (node == NULL){perror("malloc fail!");return;}node->val = x;//此时节点已经创建成功,开始往队列中插入if (pq->phead == NULL){pq->phead = pq->ptail = node;}else{pq->ptail->next = node;pq->ptail = node;}pq->size++;
}//对头删除
void QueuePop(Que* pq)
{assert(pq);assert(pq->size > 0);if (pq->phead == pq->ptail){free(pq->phead);pq->phead = NULL;pq->ptail = NULL;}else{QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;
}//取队头的数据
QDataType QueueFront(Que* pq)
{assert(pq);assert(pq->size > 0);return pq->phead->val;
}//取队尾的数据
QDataType QueueBack(Que* pq)
{assert(pq);assert(pq->size > 0);return pq->ptail->val;
}//判断队列是否为空
bool QueueEmpty(Que* pq)
{return pq->size == 0;
}//取队列节点的个数
int QueueSize(Que* pq)
{return pq->size;
}
//二叉树的层序遍历
void TreeLevalOrder(BTNode* root)
{if (root == NULL){return;}//此时root肯定不是空,则将根节点插入队列中Que q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->data);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}}
}
1.11 判断二叉树是否为完全二叉树
//判断一个二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{if (root == NULL){return false;}Que q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}QueuePush(&q, front->left);QueuePush(&q, front->right);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}