数据结构:二叉树

一、树的概述

1、树的定义

树是一种非线性的数据结构,它由节点(Node)和边(Edge)组成。树的定义如下:

(1) 节点(Node):树中的每个元素称为节点,每个节点可以有零个或多个子节点。节点可以包含数据(payload)以及指向其子节点的指针。

(2) 根节点(Root):树中的顶层节点称为根节点,它是树的起点,没有父节点。

(3) 子节点(Children):树中每个节点可以有零个或多个子节点。父节点指向其子节点,子节点指向其父节点。

(4) 叶节点(Leaf):没有子节点的节点称为叶节点,也称为终端节点。

(5) 父节点(Parent):每个节点除了根节点外,都有一个父节点,父节点指向其子节点。

(6) 兄弟节点(Siblings):具有相同父节点的节点被称为兄弟节点。

(7) 子树(Subtree):树中的任意节点和它的后代节点构成一个子树。

(8) 深度(Depth):节点到根节点的边的数量称为节点的深度,根节点的深度为0。

(9) 高度(Height):节点到其最远叶节点的边的数量称为节点的高度。

树可以具有不同的特性和类型,例如二叉树、平衡树、二叉搜索树等。树的定义提供了一种组织数据的方式,使得数据的检索和插入操作更高效。树在许多应用中都有广泛的应用,包括文件系统、数据库索引、图算法等。

2、树的存储结构

数据结构中的树可以使用不同的存储结构来表示,其中最常见的有以下几种:

(1) 链式存储结构(Linked structure):每个节点使用一个数据结构来表示,节点包含数据以及指向其子节点的指针。每个节点通常还包含一个指向其父节点的指针(除了根节点)。通过这种方式,树的结构可以通过节点之间的指针链接来表示。链式存储结构的优点是易于实现和维护,适用于动态变化的树结构。

(2) 数组存储结构(Array structure):使用数组来表示树的结构。在数组中,每个元素存储了节点的数据,通过数组的索引关系来表示节点之间的层次关系。一般来说,数组中索引为i的元素的左子节点在索引2i处,右子节点在索引2i+1处。数组存储结构的优点是节省空间,适用于静态或已知大小的树结构。

选择适当的存储结构取决于树的特性、应用需求以及对空间和时间复杂度的要求。不同的存储结构在空间和时间复杂度上可能有不同的优劣,并且对于特定问题可能会有更高效的解决方案。因此,在实际应用中,需要根据具体情况选择最适合的存储结构。

二、二叉树

1、二叉树介绍

(1)二叉树及其性质

二叉树是一种特殊的树状数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以为空树,或者由若干个节点组成,其中每个节点最多有一个父节点。

二叉树的性质包括以下几点:

【1】每个节点最多有两个子节点:二叉树中的每个节点最多有两个子节点,分别为左子节点和右子节点。如果节点没有子节点,则称为叶节点。

【2】左子树和右子树是二叉树:二叉树的子树也是二叉树,即左子树和右子树都是二叉树。这意味着可以通过递归方式定义和操作二叉树的结构。

【3】二叉树可以是空树:二叉树可以为空树,即没有任何节点。空树是一种特殊的二叉树,也是合法的二叉树。

【4】二叉树的高度:二叉树的高度是指从根节点到最远叶节点的路径上经过的节点数。空树的高度为0,只有根节点的二叉树的高度为1。

【5】二叉树的深度:二叉树的深度是指从根节点到当前节点的路径上经过的节点数。根节点的深度为0,深度为d的节点的子节点的深度为d+1。

【6】二叉树的遍历:二叉树的遍历是指按照一定的顺序访问二叉树的所有节点。常见的遍历方式包括前序遍历、中序遍历和后序遍历。前序遍历先访问根节点,然后按照左子树、右子树的顺序遍历;中序遍历先访问左子树,然后访问根节点,最后访问右子树;后序遍历先访问左子树,然后访问右子树,最后访问根节点。

【7】二叉搜索树:二叉搜索树是一种特殊的二叉树,其中每个节点的值大于其左子树的所有节点的值,小于其右子树的所有节点的值。通过这种有序性质,可以在二叉搜索树中高效地进行搜索、插入和删除操作。

【8】完全二叉树:完全二叉树是一种特殊的二叉树,除了最后一层的叶节点可能不满外,其他层的节点数都达到最大值,并且最后一层的叶节点从左到右连续排列。

【9】满二叉树:满二叉树是一种特殊的完全二叉树,每个节点都有两个子节点。满二叉树的叶节点只能出现在最后一层,且最后一层的叶节点数量为2的n次方。

以上是二叉树的一些基本概念及其性质。二叉树由于其简单的结构和丰富的性质,在计算机科学中被广泛应用,包括搜索算法、排序算法、图算法等领域。

(2) 二叉树的存储结构

二叉树的存储结构有多种方式,主要包括链式存储和顺序存储两种。

【1】链式存储:链式存储是最常见的二叉树存储方式,每个节点通过指针连接其左右子节点。每个节点通常包含三个字段:一个数据域用于存储节点的数据,一个指向左子节点的指针,一个指向右子节点的指针。使用链式存储结构的优点是易于插入和删除节点,但是访问节点需要通过指针遍历。

【2】顺序存储:顺序存储是将二叉树的节点按照某种方式存储在一个线性数组中。具体存储方式有两种:

   - 完全二叉树顺序存储:对于完全二叉树,可以使用数组来存储。将根节点存储在数组的第一个位置,然后按照从上到下、从左到右的顺序依次存储其他节点。如果一个节点的索引是i,它的左子节点的索引是2i,右子节点的索引是2i+1。通过这种方式,可以利用数组的随机访问特性快速访问节点,但是插入和删除节点需要移动其他节点的位置。

   - 二叉搜索树顺序存储:对于二叉搜索树,可以使用有序数组来存储。将节点按照中序遍历的顺序存储在数组中,这样可以保持节点的有序性。通过二分查找的方式可以快速定位节点,但是插入和删除节点需要移动其他节点的位置。

(3) 二叉树的创建

#include <stdio.h>

#include <stdlib.h>

// 定义二叉树节点结构

struct TreeNode {

    int data;

    struct TreeNode* left;

    struct TreeNode* right;

};

// 创建二叉树

struct TreeNode* createBinaryTree() {

    int data;

    struct TreeNode* newNode;

    printf("输入节点的值(输入-1表示空节点):");

    scanf("%d", &data);

    if (data == -1) {

        return NULL;

    }

    // 创建新节点

    newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));

    newNode->data = data;

    printf("输入节点 %d 的左子节点:\n", data);

    newNode->left = createBinaryTree();

    printf("输入节点 %d 的右子节点:\n", data);

    newNode->right = createBinaryTree();

    return newNode;

}

// 前序遍历二叉树

void preOrderTraversal(struct TreeNode* node) {

    if (node != NULL) {

        printf("%d ", node->data);

        preOrderTraversal(node->left);

        preOrderTraversal(node->right);

    }

}

int main() {

    struct TreeNode* root;

  

    printf("创建二叉树:\n");

    root = createBinaryTree();

  

    printf("\n前序遍历结果:");

    preOrderTraversal(root);

  

    return 0;

}

运行结果示例:

创建二叉树:

输入节点的值(输入-1表示空节点):1

输入节点 1 的左子节点:

输入节点的值(输入-1表示空节点):2

输入节点 2 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 2 的右子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 1 的右子节点:

输入节点的值(输入-1表示空节点):3

输入节点 3 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 3 的右子节点:

输入节点的值(输入-1表示空节点):4

输入节点 4 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 4 的右子节点:

输入节点的值(输入-1表示空节点):-1

前序遍历结果:1 2 3 4

在示例程序中,我们通过递归方式创建了一个二叉树,并实现了前序遍历。用户输入节点的值时,-1表示空节点。程序根据输入的值递归创建二叉树,然后输出前序遍历结果。

2、二叉树的遍历

(1)前序遍历

二叉树的前序遍历是一种遍历二叉树的方法,它的访问顺序是先访问根节点,然后按照先左后右的顺序访问左子树和右子树。具体步骤如下:

【1】 如果当前节点为空,则返回。

【2】 访问当前节点。

【3】 递归地前序遍历左子树。

【4】 递归地前序遍历右子树。

以下是一个使用C语言创建二叉树并进行前序遍历的示例程序:

#include <stdio.h>

#include <stdlib.h>

// 定义二叉树节点结构

struct TreeNode {

    int data;

    struct TreeNode* left;

    struct TreeNode* right;

};

// 创建二叉树

struct TreeNode* createBinaryTree() {

    int data;

    struct TreeNode* newNode;

    printf("输入节点的值(输入-1表示空节点):");

    scanf("%d", &data);

    if (data == -1) {

        return NULL;

    }

    // 创建新节点

    newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));

    newNode->data = data;

    printf("输入节点 %d 的左子节点:\n", data);

    newNode->left = createBinaryTree();

    printf("输入节点 %d 的右子节点:\n", data);

    newNode->right = createBinaryTree();

    return newNode;

}

// 前序遍历二叉树

void preOrderTraversal(struct TreeNode* node) {

    if (node != NULL) {

        // 访问当前节点

        printf("%d ", node->data);

        

        // 递归前序遍历左子树

        preOrderTraversal(node->left);

        

        // 递归前序遍历右子树

        preOrderTraversal(node->right);

    }

}

int main() {

    struct TreeNode* root;

  

    printf("创建二叉树:\n");

    root = createBinaryTree();

  

    printf("\n前序遍历结果:");

    preOrderTraversal(root);

  

    return 0;

}

运行结果:

创建二叉树:

输入节点的值(输入-1表示空节点):1

输入节点 1 的左子节点:

输入节点的值(输入-1表示空节点):2

输入节点 2 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 2 的右子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 1 的右子节点:

输入节点的值(输入-1表示空节点):3

输入节点 3 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 3 的右子节点:

输入节点的值(输入-1表示空节点):4

输入节点 4 的左子节点:

输入节点的值(输入-1表示空节点):-1

输入节点 4 的右子节点:

输入节点的值(输入-1表示空节点):-1

前序遍历结果:1 2 3 4

在示例程序中,我们通过递归方式创建了一个二叉树,并实现了前序遍历。用户输入节点的值时,-1表示空节点。程序根据输入的值递归创建二叉树,然后输出前序遍历结果。

(2)中序遍历

二叉树的中序遍历是一种遍历二叉树的方法,它的访问顺序是按照先左后根再右的顺序遍历二叉树的节点。具体步骤如下:

【1】 如果当前节点为空,则返回。

【2】 递归地中序遍历左子树。

【3】 访问当前节点。

【4】 递归地中序遍历右子树。

以下是一个使用C语言创建二叉树并进行中序遍历的示例程序:

#include <stdio.h>

#include <stdlib.h>

// 定义二叉树节点结构

struct TreeNode {

    int data;

    struct TreeNode* left;

    struct TreeNode* right;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字化与智能化

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值