在数据结构与算法中,树和森林是非常重要的概念。这里我会分步骤详细解释这些概念,并提供C语言的代码案例以及实际生活中的应用示例。
1. 树的存储
树的存储方式主要有两种:顺序存储和链式存储。
顺序存储:通常用于完全二叉树,通过数组实现,每个节点的孩子或父亲的位置可以通过索引计算得到。
链式存储:每个节点包括数据部分和若干指向其子节点的指针。以下是一个简单的二叉树链式存储结构的示例:
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// 创建一个新节点
TreeNode* createNode(int data) {
TreeNode *newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode) {
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
}
return newNode;
}
// 示例:创建一个简单的树
TreeNode* createExampleTree() {
TreeNode *root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
return root;
}
实际生活中的应用:例如,文件系统中的文件夹结构可以通过树结构进行存储,其中每个节点代表一个文件夹或文件。
2. 树、森林与二叉树的相互转换
树和森林可以转换为二叉树进行处理。这种转换可以简化某些算法的实现,比如遍历。
树转换为二叉树:将每个节点的第一个孩子作为二叉树中的左孩子,将其余孩子依次作为二叉树中左孩子的右孩子。
TreeNode* convertTreeToBinary(TreeNode *root) {
if (!root) return NULL;
// 处理所有子节点,将它们转换成一个链表(右孩子)
TreeNode *last = NULL;
for (TreeNode *child = root->firstChild; child != NULL; child = child->nextSibling) {
if (last)
last->right = convertTreeToBinary(child);
else
root->left = convertTreeToBinary(child);
last = child;
}
return root;
}
实际生活中的应用:组织结构图通常是一个森林,可以转换成二叉树来进行更有效的管理和遍历。
3. 树和森林的遍历
树的遍历有四种主要方式:前序、中序、后序以及层次遍历。森林的遍历可以通过将森林转换为二叉树后进行类似的遍历。
// 前序遍历
void preorder(TreeNode *root) {
if (root) {
printf("%d ", root->data);
preorder(root->left);
preorder(root->right);
}
}
// 中序遍历
void inorder(TreeNode *root) {
if (root) {
inorder(root->left);
printf("%d ", root->data);
inorder(root->right);
}
}
// 后序遍历
void postorder(TreeNode *root) {
if (root) {
postorder(root->left);
postorder(root->right);
printf("%d ", root->data);
}
}
实际生活中的应用:比如解析算术表达式时,可以建立一个表达式树,然后通过不同的遍历顺序得到前缀表达式、中缀表达式或后缀表达式。