数据结构与C

数据结构与C 知识量:8 - 24 - 99

5.2 二叉树><

二叉树的定义- 5.2.1 -

二叉树是每个节点最多只有两棵子树的树结构,有左右之分。二叉树是n个有限元素的集合,该集合或者为空,或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点。

满二叉树- 5.2.2 -

满二叉树是指除最后一层外,每一层上的所有结点都有两个子结点的二叉树。也就是说,如果一个二叉树的深度为K,且结点总数是2^k - 1,则它就是满二叉树。

完全二叉树- 5.2.3 -

完全二叉树是指除最后一层外,每一层上的所有结点都有两个子结点的二叉树。也就是说,如果一个二叉树的深度为h,除第h层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。完全二叉树是由满二叉树而引出来的。 对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

二叉树的主要性质- 5.2.4 -

二叉树的主要性质包括:

  • 每个节点最多有两个子节点,通常称为左子节点和右子节点。

  • 树是递归定义的,每个节点必须有一个父节点(除了根节点)。

  • 每个节点有0个或1个左子节点,0个或1个右子节点。

  • 根节点是唯一没有父节点的节点。

  • 在二叉树中,除了根节点外,每个节点都有且仅有一个父节点。

  • 二叉树中的每个节点都至少有一个子节点(可以是左子节点或右子节点)。

  • 二叉树的深度是其最深叶子节点的层数。

  • 对于具有n个节点的二叉树,其深度至少为 log2(n+1) 。

  • 具有n个节点的完全二叉树的深度为 log2(n) 。

  • 对于具有n个节点的二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的节点有:若2i+1<n,左孩子序号:2i+1,否则无左孩子。

二叉树的存储结构- 5.2.5 -

二叉树的存储结构可以用多种方式表示,其中最常用的方式是使用数组或链表。在C语言中,通常使用结构体来表示二叉树的节点,然后使用数组或链表来存储这些节点。

以下是使用数组表示二叉树的一种方法:

#include <stdio.h>  
#include <stdlib.h>  
  
#define MAX_TREE_SIZE 100 // 最大树节点数  
  
// 二叉树节点结构体  
typedef struct TreeNode {  
    int data; // 节点数据  
    int leftChildIndex; // 左孩子节点在数组中的索引  
    int rightChildIndex; // 右孩子节点在数组中的索引  
} TreeNode;  
  
// 初始化二叉树  
void initTree(TreeNode *tree, int size) {  
    for (int i = 0; i < size; i++) {  
        tree[i].leftChildIndex = -1; // 初始时,所有节点的左孩子为空  
        tree[i].rightChildIndex = -1; // 初始时,所有节点的右孩子为空  
    }  
}  
  
// 添加节点到二叉树  
void addNode(TreeNode *tree, int parentIndex, int childIndex) {  
    if (parentIndex < 0 || parentIndex >= MAX_TREE_SIZE) {  
        printf("Parent index out of range!\n");  
        return;  
    }  
    if (childIndex < 0 || childIndex >= MAX_TREE_SIZE) {  
        printf("Child index out of range!\n");  
        return;  
    }  
    if (tree[parentIndex].leftChildIndex == -1) {  
        tree[parentIndex].leftChildIndex = childIndex;  
    } else {  
        tree[parentIndex].rightChildIndex = childIndex;  
    }  
}  
  
// 打印二叉树(中序遍历)  
void printTree(TreeNode *tree, int rootIndex) {  
    if (rootIndex < 0 || rootIndex >= MAX_TREE_SIZE) {  
        printf("Root index out of range!\n");  
        return;  
    }  
    printTreeHelper(tree, rootIndex, 0); // 从根节点开始,递归打印二叉树(中序遍历)  
}  
  
void printTreeHelper(TreeNode *tree, int index, int depth) {  
    if (index < 0 || index >= MAX_TREE_SIZE) {  
        printf("Index out of range!\n");  
        return;  
    }  
    for (int i = 0; i < depth; i++) printf("\t"); // 根据深度打印缩进(模拟树的层次结构)  
    printf("%d\n", tree[index].data); // 打印节点数据  
    if (tree[index].leftChildIndex != -1) { // 如果左孩子不为空,递归打印左子树  
        printTreeHelper(tree, tree[index].leftChildIndex, depth + 1);  
    }  
    if (tree[index].rightChildIndex != -1) { // 如果右孩子不为空,递归打印右子树  
        printTreeHelper(tree, tree[index].rightChildIndex, depth + 1);  
    }  
}

以上代码定义了一个TreeNode结构体来表示二叉树的节点,每个节点包含一个数据字段和两个索引字段,分别表示左孩子和右孩子的索引。initTree函数用于初始化所有节点的左孩子和右孩子为空。addNode函数用于添加一个节点到二叉树中。printTree函数用于打印二叉树(中序遍历)。printTreeHelper函数是一个递归辅助函数,用于实际执行中序遍历。

二叉树的遍历- 5.2.6 -

二叉树的遍历主要有三种方法:前序遍历、中序遍历和后序遍历。以下是使用C语言实现这三种遍历的代码:

首先,需要定义一个二叉树节点的结构体:

typedef struct TreeNode {  
    int val;  
    struct TreeNode *left;  
    struct TreeNode *right;  
} TreeNode;

1. 前序遍历(Preorder Traversal):首先访问根节点,然后递归地进行左子树的先序遍历,接着递归地进行右子树的先序遍历。

void preorderTraversal(TreeNode *root) {  
    if (root == NULL) {  
        return;  
    }  
    printf("%d ", root->val); // 访问根节点  
    preorderTraversal(root->left); // 递归进行左子树的先序遍历  
    preorderTraversal(root->right); // 递归进行右子树的先序遍历  
}

2. 中序遍历(Inorder Traversal):首先递归地进行左子树的中序遍历,然后访问根节点,最后递归地进行右子树的中序遍历。

void inorderTraversal(TreeNode *root) {  
    if (root == NULL) {  
        return;  
    }  
    inorderTraversal(root->left); // 递归进行左子树的中序遍历  
    printf("%d ", root->val); // 访问根节点  
    inorderTraversal(root->right); // 递归进行右子树的中序遍历  
}

3. 后序遍历(Postorder Traversal):首先递归地进行左子树的后序遍历,然后递归地进行右子树的后序遍历,最后访问根节点。

void postorderTraversal(TreeNode *root) {  
    if (root == NULL) {  
        return;  
    }  
    postorderTraversal(root->left); // 递归进行左子树的后序遍历  
    postorderTraversal(root->right); // 递归进行右子树的后序遍历  
    printf("%d ", root->val); // 访问根节点  
}

以上代码中,使用了递归的方式来实现二叉树的遍历。在实际应用中,也可以使用非递归的方式来实现,这通常需要使用到栈来辅助完成。