数据结构之链式二叉树详解

数据结构之链式二叉树详解

链式二叉树是一种基于链表的二叉树存储实现方式,相对于基于数组的存储方式更加灵活。本文将详细讲解如何实现链式二叉树及相关操作。

数据结构定义

链式二叉树的节点定义如下:

template<class T>
struct BinaryTreeNode
{
    T m_nValue; // 节点的值
    BinaryTreeNode* m_pLeft; // 左子节点
    BinaryTreeNode* m_pRight; // 右子节点
};

链式二叉树的定义如下:

template<class T>
class BinarySearchTree
{
public:
    BinarySearchTree();
    ~BinarySearchTree();

    void insert(const T& value); // 插入节点
    void remove(const T& value); // 删除节点
    BinaryTreeNode<T>* find(const T& value); // 查找节点
    BinaryTreeNode<T>* minimum() const; // 返回最小值节点
    BinaryTreeNode<T>* maximum() const; // 返回最大值节点
    void print() const; // 中序遍历
private:
    void destroy_tree(BinaryTreeNode<T>* leaf); // 删除树节点
    void insert(const T& value, BinaryTreeNode<T>*& leaf); // 插入节点
    void remove(const T& value, BinaryTreeNode<T>*& leaf); // 删除节点
    BinaryTreeNode<T>* find(const T& value, BinaryTreeNode<T>* leaf); // 查找节点
    BinaryTreeNode<T>* minimum(BinaryTreeNode<T>* leaf) const; // 返回最小值节点
    BinaryTreeNode<T>* maximum(BinaryTreeNode<T>* leaf) const; // 返回最大值节点
    void print(BinaryTreeNode<T>* leaf) const; // 中序遍历
    BinaryTreeNode<T>* root_;
};

插入节点

插入操作是链式二叉树中比较简单的操作,比较文本值,根据大小选择左子节点或右子节点进行插入。根据文本值大小判断节点的左右子树,若某个节点的左子树或右子树为空,则在该位置插入节点,否则利用递归进行插入操作。

代码实现如下:

template<class T>
void BinarySearchTree<T>::insert(const T& value)
{
    insert(value, root_);
}

template<class T>
void BinarySearchTree<T>::insert(const T& value, BinaryTreeNode<T>*& leaf)
{
    if (leaf == nullptr) {
        leaf = new BinaryTreeNode<T>();
        leaf->m_nValue = value;
        leaf->m_pLeft = nullptr;
        leaf->m_pRight = nullptr;
    } else if (value < leaf->m_nValue) {
        insert(value, leaf->m_pLeft);
    } else {
        insert(value, leaf->m_pRight);
    }
}

删除节点

删除操作需要分为三种情况:

  • 节点为叶子节点,直接删除即可。
  • 节点有一个子节点,将子节点替换该节点即可。
  • 节点有两个子节点,先找出该节点右子树中的最小值,将其替换该节点,再删除该最小值节点。

代码实现如下:

template<class T>
void BinarySearchTree<T>::remove(const T& value)
{
    remove(value, root_);
}

template<class T>
void BinarySearchTree<T>::remove(const T& value, BinaryTreeNode<T>*& leaf)
{
    if (leaf == nullptr) {
        return;
    }
    if (value < leaf->m_nValue) {
        remove(value, leaf->m_pLeft);
    } else if (value > leaf->m_nValue) {
        remove(value, leaf->m_pRight);
    } else {
        if (leaf->m_pLeft == nullptr && leaf->m_pRight == nullptr) {
            delete leaf;
            leaf = nullptr;
        } else if (leaf->m_pLeft == nullptr) {
            BinaryTreeNode<T>* temp = leaf->m_pRight;
            delete leaf;
            leaf = temp;
        } else if (leaf->m_pRight == nullptr) {
            BinaryTreeNode<T>* temp = leaf->m_pLeft;
            delete leaf;
            leaf = temp;
        } else {
            BinaryTreeNode<T>* temp = minimum(leaf->m_pRight);
            leaf->m_nValue = temp->m_nValue;
            remove(temp->m_nValue, leaf->m_pRight);
        }
    }
}

查找节点

查找操作也是基于链式二叉树的特点,利用文本值大小进行查找,若该节点值大于(小于)查找值,则在左子树中(右子树中)进行查找,否则查找值即为该节点值,返回之。

代码实现如下:

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::find(const T& value)
{
    return find(value, root_);
}

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::find(const T& value, BinaryTreeNode<T>* leaf)
{
    if (leaf == nullptr) {
        return nullptr;
    }
    if (value < leaf->m_nValue) {
        return find(value, leaf->m_pLeft);
    } else if (value > leaf->m_nValue) {
        return find(value, leaf->m_pRight);
    } else {
        return leaf;
    }
}

返回最小值节点和最大值节点

返回最小值节点和最大值节点同样是基于链式二叉树的特点,分别在左子树上和右子树上不断向下寻找,直到到达叶子节点。

代码实现如下:

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::minimum() const
{
    return minimum(root_);
}

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::minimum(BinaryTreeNode<T>* leaf) const
{
    if (leaf == nullptr) {
        return nullptr;
    }
    if (leaf->m_pLeft == nullptr) {
        return leaf;
    } else {
        return minimum(leaf->m_pLeft);
    }
}

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::maximum() const
{
    return maximum(root_);
}

template<class T>
BinaryTreeNode<T>* BinarySearchTree<T>::maximum(BinaryTreeNode<T>* leaf) const
{
    if (leaf == nullptr) {
        return nullptr;
    }
    if (leaf->m_pRight == nullptr) {
        return leaf;
    } else {
        return maximum(leaf->m_pRight);
    }
}

中序遍历

中序遍历是将整棵树按照从小到大的顺序输出。

template<class T>
void BinarySearchTree<T>::print() const
{
    print(root_);
    std::cout << std::endl;
}

template<class T>
void BinarySearchTree<T>::print(BinaryTreeNode<T>* leaf) const
{
    if (leaf == nullptr) {
        return;
    }
    print(leaf->m_pLeft);
    std::cout << leaf->m_nValue << " ";
    print(leaf->m_pRight);
}

示例说明

BinarySearchTree<int> t;
t.insert(5);
t.insert(3);
t.insert(2);
t.insert(4);
t.insert(7);
t.insert(6);
t.insert(8);
t.print(); // 2 3 4 5 6 7 8
t.remove(5);
t.print(); // 2 3 4 6 7 8
t.find(6); // 返回6节点
t.minimum(); // 返回值为2的节点
t.maximum(); // 返回值为8的节点

以上代码示例展示了链式二叉树的插入、删除、查找、返回最小值节点、返回最大值节点及中序遍历操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:数据结构之链式二叉树详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Vue2父子组件传值举例详解

    Vue2父子组件传值举例详解 在Vue2中,父子组件之间的数据传递是非常常见的需求。本攻略将详细讲解Vue2中父子组件传值的方法,并提供两个示例说明。 Props Props是Vue中父组件向子组件传递数据的一种方式。父组件通过props属性将数据传递给子组件,子组件通过props接收数据并使用。 示例1:父组件向子组件传递数据 父组件的代码如下: <…

    other 2023年8月19日
    00
  • 原神流浪者武器优先级选择攻略 流浪者武器排行推荐

    原神流浪者武器优先级选择攻略 流浪者是游戏《原神》中的一名弓箭手角色,在游戏中使用弓箭进行远程攻击。选择适合流浪者的武器是提升其攻击力和输出的关键。以下是你需要了解的流浪者武器攻略。 流浪者武器的种类 目前在游戏中可以选择的武器类型包括弓箭、长柄武器、单手剑及双手剑。而针对流浪者这个角色,适用的武器类型为弓箭。 流浪者武器属性评估指标 主属性 流浪者武器的攻…

    other 2023年6月27日
    00
  • 解决python递归函数及递归次数受到限制的问题

    解决 Python 递归函数及递归次数受到限制的问题有两种方法,分别为手动设置递归深度和使用尾递归。 手动设置递归深度 Python 中的默认递归深度为 1000,所以如果超出了默认深度时就会抛出递归异常。我们可以使用 sys 模块来手动设置递归深度。 import sys sys.setrecursionlimit(3000) # 修改递归深度为 3000…

    other 2023年6月27日
    00
  • win10英雄联盟图形设备初始化失败如何解决?

    当玩家在使用Windows 10操作系统时,在运行英雄联盟游戏时可能会遇到“图形设备初始化失败”的问题。这个问题通常出现在电脑的显卡驱动程序上。以下是解决这个问题的攻略: 步骤一:检查显卡驱动程序是否安装或过期 如果你碰到了“图形设备初始化失败”的问题,首先要检查显卡驱动程序是否安装或已过期。以下是解决这个问题的步骤: 按下Windows键+R来打开运行窗口…

    other 2023年6月20日
    00
  • SQL字段拆分优化

    SQL字段拆分优化是指在数据库设计和查询过程中,将一个大字段拆分成多个小字段,以便于查询和维护。这个优化技巧可以有效地提高数据库的性能和可维护性。 以下是SQL字段拆分优化的完整攻略: 1. 分析大字段的数据结构和使用场景 在对大字段进行拆分之前,我们需要先了解这个大字段的数据结构和使用场景。例如,如果这个大字段包含的是一个JSON对象,那么我们可以将这个J…

    other 2023年6月25日
    00
  • Java关于含有继承类的成员初始化过程讲解

    Java关于含有继承类的成员初始化过程讲解 在Java中,含有继承类的成员初始化过程比较复杂。本文将从以下几个方面详细讲解初始化过程:继承、实例化、构造函数和静态变量初始化。通过多个示例的说明,让读者更加深入地理解Java中含有继承类的成员初始化过程。 继承 在Java中,子类继承了父类的属性和方法,但是并不包括构造函数。因此,在实例化子类时,需要先实例化父…

    other 2023年6月20日
    00
  • vue异步延时执行

    Vue异步延时执行的攻略 在Vue中,我们经常需要在异步操作中延时执行某些代码。本攻略将详细介绍Vue中异步延的方法,并提供两个示例。 方法1:使用setTimeout函数 我们可以使用JavaScript中的setTimeout函数来实现异步延时执行。以下是体步骤: 在Vue组件中定义一个方法,该方法包含需要延时执行的代码。 在该方法中使用setTimeo…

    other 2023年5月9日
    00
  • android实现模拟加载中的效果

    实现模拟加载中的效果,一般可以通过以下方式实现: 方法一:使用ProgressDialog ProgressDialog是Android内置的一种对话框,可以方便地实现加载中的效果。 步骤一:创建ProgressDialog 在需要展示加载中效果的Activity中,创建ProgressDialog,并设置相关参数。 ProgressDialog progr…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部