二叉树的基本概念及操作实现(C++)

1 二叉树的基本概念

1.1 二叉树的定义

二叉树是一种特殊的树形数据结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树在计算机科学中有着广泛的应用,包括但不限于表达式解析、文件系统、搜索算法等。以下是二叉树的一些基本定义和特性:

  1. 节点定义:二叉树由节点组成,每个节点包含数据和两个指向其子节点的指针(左子节点和右子节点)。

  2. 根节点:二叉树只有一个根节点,它是树的起始点,没有父节点。

  3. 叶子节点:没有子节点的节点称为叶子节点或外部节点。

  4. 内部节点:至少有一个子节点的节点称为内部节点或非终端节点。

  5. 子树:由一个节点和其所有后代组成的树称为该节点的子树。

  6. 深度:二叉树的深度是最大的节点层数,从根节点到最远叶子节点的最长路径。

  7. 高度:二叉树的高度是从根节点到最远叶子节点的边的数量。在某些定义中,根节点的高度可能被视为0或1。

  8. 满二叉树:如果除了叶子节点外,所有节点都恰好有两个子节点,则称为满二叉树。

  9. 完全二叉树:如果所有层都被完全填满,并且最后一层的节点尽可能地集中在左侧,则称为完全二叉树。

  10. 平衡二叉树:左右子树的高度差不超过1的二叉树,例如AVL树。

  11. 二叉搜索树(BST):一种特殊的二叉树,其中每个节点的值大于其左子树中的所有节点的值,小于其右子树中的所有节点的值。

1.2 二叉树的性质

二叉树常见的性质如下:

  1. 有序性:左子节点的值总是小于或等于其父节点的值,而右子节点的值总是大于或等于其父节点的值。这种特性使得二叉树特别适合实现二叉搜索树。

  2. 递归结构:二叉树的每个节点(除了根节点)都可以看作是其子树的根。

  3. 动态性:二叉树的大小可以动态变化,节点可以在任何时候被添加或删除。

  4. 存储结构:二叉树通常使用链式存储结构实现,每个节点包含数据和指向其子节点的指针。

  5. 遍历方式:二叉树可以通过多种方式进行遍历,包括前序、中序、后序和层序遍历。

  6. 平衡性:在某些类型的二叉树(如AVL树)中,保持树的平衡是重要的,以确保操作的效率。

1.3 二叉树的存储结构

二叉树的存储结构主要有两种方式:数组链表。链式存储结构在插入和删除操作中更加灵活,而顺序存储结构则在遍历和访问节点时更加高效。

(1)链式存储结构(链表)

  • 链式存储是二叉树最常用的存储方式。

  • 每个节点通常包含三个字段:数据域和两个指针域(分别指向左子节点和右子节点)。

  • 节点通过指针域链接起来,形成一个非线性的数据结构。

    typedef struct TreeNode {
      ElementType data;  // 数据域,存储节点的数据
      TreeNode *left;     // 左子节点的指针域
      TreeNode *right;    // 右子节点的指针域
    } TreeNode;
    

(2)顺序存储结构(数组)

  • 顺序存储通常使用数组来实现,特别是在完全二叉树的情况下。

  • 节点按照从上到下、从左到右的顺序存储在数组中。

  • 给定一个节点的索引i,其左子节点的索引为2i+1,右子节点的索引为2i+2(如果存在)。

  • 同样地,给定一个节点的索引i,其父节点的索引可以通过(i-1)/2计算得出(如果i不为0)。

    // 假设使用数组存储完全二叉树
    TreeNode array[n]; // n是数组的大小,即树中节点的最大数量
    

(3)二叉树的数组表示

  • 在数组中,可以通过节点的索引来快速定位其父节点和子节点。
  • 这种存储方式特别适用于完全二叉树,因为它们可以完美地填充数组的前n个位置。

(4)二叉树的链表表示

  • 链表表示允许二叉树不必是完全二叉树,因为它不需要额外的空间来存储不存在的节点。
  • 链表表示的二叉树可以动态地增长和收缩,而不需要重新分配数组。

(5)二叉堆(特殊二叉树)

  • 二叉堆是一种特殊的二叉树,通常使用数组来实现。
  • 它满足堆的性质,即父节点的值总是大于(或小于)其子节点的值。

2 二叉树的基本操作及实现

  • 二叉树的基本操作通常包括以下几个方面:
  1. 创建节点:创建一个新的二叉树节点。
  2. 插入节点:在二叉搜索树中根据值的顺序插入新节点。
  3. 删除节点:从二叉树中移除一个节点。
  4. 查找节点:在二叉树中查找特定值的节点。
  5. 遍历树:按照特定的顺序访问树中的所有节点,常见的遍历方式有前序、中序、后序和层序遍历。
  6. 获取树的高度:计算二叉树的高度,通常用于平衡树的调整。
  7. 判断树是否平衡:对于平衡二叉树,需要检查树是否满足平衡条件。

以下是C++中定义二叉树节点和实现基本操作的简单示例:

#include <iostream>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 假设这里实现了其他二叉树操作,比如插入、删除等

int main() {
    // 创建一个简单的二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    // 这里可以调用函数

    return 0;
}

2.1 创建节点

TreeNode 类或结构体定义了二叉树节点的基本结构,包括存储数据的变量以及指向子节点的指针。具体实现如下:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

2.2 插入节点

对于二叉搜索树(BST),插入新节点的步骤如下:

  1. 创建新节点:创建一个新的TreeNode实例,其值为要插入的值。
  2. 找到插入位置:从根节点开始,递归地遍历树,直到找到一个空位(即一个nullptr)。
  3. 插入节点:将新节点插入到找到的空位。
TreeNode* insert(TreeNode* node, int val) {
    // 如果当前节点为空,返回一个新节点
    if (node == nullptr) {
        return new TreeNode(val);
    }
    // 如果值小于当前节点的值,向左子树插入
    if (val < node->val) {
        node->left = insert(node->left, val);
    }
    // 如果值大于或等于当前节点的值,向右子树插入
    else {
        node->right = insert(node->right, val);
    }
    return node;
}

2.3 删除节点

删除节点稍微复杂一些,需要考虑以下几种情况:

  1. 删除叶节点:如果节点没有子节点,直接删除该节点。
  2. 删除只有一个子节点的节点:删除该节点,并用其子节点替换它。
  3. 删除有两个子节点的节点:找到该节点的直接前驱或直接后继(通常是其右子树中的最小值或左子树中的最大值),用这个节点的值替换要删除的节点的值,然后删除那个前驱或后继。

以下是删除节点的示例代码:

TreeNode* deleteNode(TreeNode* root, int key) {
    if (!root) return root;
    if (key < root->val) root->left = deleteNode(root->left, key);
    else if (key > root->val) root->right = deleteNode(root->right, key);
    else {
        if (!root->left) return root->right;
        if (!root->right) return root->left;
        TreeNode* temp = findMin(root->right);
        root->val = temp->val;
        root->right = deleteNode(root->right, temp->val);
    }
    return root;
}

TreeNode* findMin(TreeNode* node) {
    while (node && node->left) node = node->left;
    return node;
}

2.4 查找节点

在二叉树中查找节点通常意味着要找到包含特定值的节点。这个过程可以通过递归或迭代的方式实现。

TreeNode* search(TreeNode* node, int val) {
    while (node != nullptr) {
        if (val == node->val) return node;
        node = val < node->val ? node->left : node->right;
    }
    return nullptr;
}

2.5 遍历树

二叉树的遍历方式主要包括:

  • 前序遍历(Pre-order):先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。
  • 中序遍历(In-order):先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。
  • 后序遍历(Post-order):先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。
  • 层序遍历(Breadth-first search,BFS):从根节点开始,按层次访问所有节点。
    下面是这四种遍历方式的基本实现。

前序遍历(Pre-order)

前序遍历的顺序是:根节点 -> 左子树 -> 右子树。

void preOrder(TreeNode* root) {
    if (root == nullptr) {
        return;
    }
    std::cout << root->val << " ";  // 访问根节点
    preOrder(root->left);           // 遍历左子树
    preOrder(root->right);          // 遍历右子树
}

中序遍历(In-order)

中序遍历的顺序是:左子树 -> 根节点 -> 右子树。

void inOrder(TreeNode* root) {
    if (root == nullptr) {
        return;
    }
    inOrder(root->left);            // 遍历左子树
    std::cout << root->val << " ";  // 访问根节点
    inOrder(root->right);           // 遍历右子树
}

后序遍历(Post-order)

后序遍历的顺序是:左子树 -> 右子树 -> 根节点。

void postOrder(TreeNode* root) {
    if (root == nullptr) {
        return;
    }
    postOrder(root->left);          // 遍历左子树
    postOrder(root->right);         // 遍历右子树
    std::cout << root->val << " ";  // 访问根节点
}

层序遍历(BFS)

层序遍历使用队列来实现,按照从上到下,从左到右的顺序遍历。

void levelOrder(TreeNode* root) {
    if (root == nullptr) {
        return;
    }
    std::queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        std::cout << node->val << " ";
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
    }
}

这些遍历方法可以用于不同的场景,例如树的复制、树的序列化和反序列化等。每种遍历方式都有其特定的应用场景和特点。

2.6 获取树的高度

二叉树的高度是指从根节点到最远叶子节点的最长路径上的边数。在计算二叉树的高度时,通常有两种定义方式:

  • 深度定义:高度是根节点到最远叶子节点的边的数量。在这种定义下,空树的高度是 -1。
  • 层级定义:高度是根节点到最远叶子节点的节点数。在这种定义下,空树的高度是 0。
int getHeight(TreeNode* node) {
    if (node == nullptr) return -1; // 返回-1表示空树的高度为-1
    return 1 + std::max(getHeight(node->left), getHeight(node->right));
}

2.7 判断树是否平衡

平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树,其中每个节点的左子树和右子树的高度差至多为1。因此,平衡二叉树的一个简单判断方法是检查树的高度差是否不超过1。

bool isBalanced(TreeNode* node) {
    if (node == nullptr) return true;
    int leftHeight = getHeight(node->left);
    int rightHeight = getHeight(node->right);
    return std::fabs(leftHeight - rightHeight) <= 1 &&
           isBalanced(node->left) &&
           isBalanced(node->right);
}
  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

良辰与日月

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

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

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

打赏作者

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

抵扣说明:

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

余额充值