数据结构基础(17) --二叉查找树的设计与实现

简介: 二叉排序树的特征二叉排序树或者是一棵空树,或者是具有如下特性的二叉树:    1.每一元素都有一个键值, 而且不允许重复;    2.若它的左子树不空,则左子树上所有结点的值均小于根结点的值;    3.若它的右子树不空,则右子树上所有结点的值均大于根结点的值;    4.它的左、右子树也都分别是二叉排序树。

二叉排序树的特征

二叉排序树或者是一棵空树,或者是具有如下特性的二叉树:

    1.每一元素都有一个键值, 而且不允许重复;

    2.若它的左子树不空,则左子树上所有结点的值均小于根结点的值;

    3.若它的右子树不空,则右子树上所有结点的值均大于根结点的值;

    4.它的左、右子树也都分别是二叉排序树。



二叉排序树保存的元素构造

template <typename Type>
class Element
{
public:
    Element(const Type& _key): key(_key) {}
    Element():key(0) {}
    Type key;
    //在这儿可以很容易的添加更多的数据
    //方便对Element进行扩展
};

二叉排序树节点的设计与实现

template <typename Type>
class BstNode
{
    friend class BsTree<Type>;

public:
    BstNode(const Element<Type> &_data = 0,
            BstNode *_leftChild = NULL,
            BstNode *_rightChild = NULL)
        : data(_data), leftChild(_leftChild), rightChild(_rightChild) {}

    const Type &getData() const
    {
        return data.key;
    }

private:
    //Node当中保存的是Element元素
    Element<Type> data;
    BstNode *leftChild;
    BstNode *rightChild;

    void display(int i);
};
//中序遍历二叉树:
//能够保证该二叉树元素按照递增顺序打印出来
template <typename Type>
void BstNode<Type>::display(int i)
{
    //首先访问左子树
    if (leftChild != NULL)
        leftChild->display(2*i);

    //访问中间节点
    //Number表示为如果该树为完全二叉树/满二叉树, 其编号为几
    std::cout << "Number: " << i << ", data.key = " << data.key << std::endl;

    //访问右子树
    if (rightChild != NULL)
        rightChild->display(2*i+1);
}

二叉排序树的构造

template <typename Type>
class BsTree
{
public:
//构造与析构
    BsTree(BstNode<Type> *init = NULL): root(init) {}
    ~BsTree()
    {
        if (!isEmpty())
            makeEmpty(root);
    }

//二叉查找树的三大主力:插入, 删除, 搜索(又加入了一个迭代搜索)
    //插入
    bool insert(const Element<Type> &item);
    //删除
    void remove(const Element<Type> &item)
    {
        remove(item, root);
    }
    //递归搜索
    const BstNode<Type>* search(const Element<Type> &item)
    {
        return search(item, root);
    }
    //迭代搜索
    const BstNode<Type> *searchByIter(const Element<Type> &item);

//实用函数
    void display() const
    {
        if (root != NULL)
            root->display(1);
    }
    void visit(BstNode<Type> * currentNode) const
    {
        std::cout << "data.key = "
                  << currentNode->data.key << std::endl;
    }
    bool isEmpty() const
    {
        return root == NULL;
    }
    void makeEmpty(BstNode<Type> *subTree);
    //中序遍历
    void levelOrder() const;

private:
    const BstNode<Type>* search(const Element<Type> &item,
                                const BstNode<Type> *currentNode);
    void remove(const Element<Type> &item,
                BstNode<Type> *¤tNode);

private:
    BstNode<Type> *root;
};

二叉排序树的插入算法

    根据动态查找表的定义,插入操作在查找不成功时才进行;若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。

//二叉排序树插入的实现与解析
template <typename Type>
bool BsTree<Type>::insert(const Element<Type> &item)
{
    //如果这是新插入的第一个节点
    if (root == NULL)
    {
        root = new BstNode<Type>(item);
        root->leftChild = root->rightChild = NULL;
        return true;
    }

    BstNode<Type> *parentNode = NULL;   //需要插入位置的父节点
    BstNode<Type> *currentNode = root;  //需要插入的位置
    while (currentNode != NULL)
    {
        //如果二叉树中已经含有了该元素, 则返回插入出错
        if (item.key == currentNode->data.key)
            return false;

        parentNode = currentNode;
        //如果要插入的元素大于当前指向的元素
        if (item.key < currentNode->data.key)
            currentNode = currentNode->leftChild;   //向左搜索
        else
            currentNode = currentNode->rightChild;  //向右搜索
    }

    //此时已经查找到了一个比较合适的插入位置了
    if (item.key < parentNode->data.key)
        parentNode->leftChild = new BstNode<Type>(item);
    else
        parentNode->rightChild = new BstNode<Type>(item);

    return true;
}

二叉排序树的查找算法

若二叉排序树为空,则查找不成功;否则:

    1.若给定值等于根结点的关键字,则查找成功;

    2.若给定值小于根结点的关键字,则继续在左子树上进行查找;

    3.若给定值大于根结点的关键字,则继续在右子树上进行查找。

//二叉排序树搜索的设计与实现
//递归搜索
template <typename Type>
const BstNode<Type>* BsTree<Type>::search(const Element<Type> &item,
        const BstNode<Type> *currentNode)
{
    if (currentNode == NULL)
        return NULL;
    if (currentNode->data.key == item.key)
        return currentNode;

    if (item.key < currentNode->data.key)
        return search(item, currentNode->leftChild);
    else
        return search(item, currentNode->rightChild);
}
//迭代搜索
template <typename Type>
const BstNode<Type> *BsTree<Type>::searchByIter(const Element<Type> &item)
{
    for (BstNode<Type> *searchNode = root;
            searchNode != NULL;
            /*empty*/)
    {
        if (item.key == searchNode->data.key)
            return searchNode;

        if (item.key < searchNode->data.key)
            searchNode = searchNode->leftChild;
        else
            searchNode = searchNode->rightChild;
    }

    return NULL;
}

二叉排序树的删除算法

    和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性

 

删除分三种情况:

    1.被删除的结点是叶子节点:其双亲结点中相应指针域的值改为“空”, 并将该节点删除;

    2.被删除的结点只有左子树或者只有右子树:其双亲结点的相应指针域的值改为 “指向被删除结点的左子树或右子树”, 然后删除该节点;

    3.被删除的结点既有左子树,也有右子树:以其前驱替代之,然后再删除该前驱结点;

//二叉排序树节点删除的实现与解析如下
template <typename Type>
void BsTree<Type>::remove(const Element<Type> &item,
                          BstNode<Type> *¤tNode)
{
    if (currentNode != NULL)
    {
        //如果要删除的元素小于当前元素
        if (item.key < currentNode->data.key)
            remove(item, currentNode->leftChild);   //向左搜索删除
        //如果要删除的元素大于当前元素
        else if (item.key > currentNode->data.key)
            remove(item, currentNode->rightChild);  //向右搜索删除
        //如果要删除掉的元素等于当前元素(找到要删除的元素了)
        // 并且当前节点的左右子女节点都不为空
        else if ((currentNode->leftChild != NULL) && (currentNode->rightChild != NULL))
        {
            //从当前节点的右子女节点开始,
            //不断向左寻找, 找到从当前节点开始中序遍历的第一个节点
            //找到的这一个节点是在当前子树中, 大于要删除的节点的第一个节点
            BstNode<Type> *tmp = currentNode->rightChild;
            while (tmp->leftChild != NULL)
                tmp = tmp->leftChild;

            //用搜索到的节点值覆盖要删除的节点值
            currentNode->data.key = tmp->data.key;
            //删除搜索到的节点
            remove(currentNode->data, currentNode->rightChild);
        }
        //如果当前节点就是要删除的节点
        //并且其左子女(和/或)右子女为空
        //默认包含了左右子女同时为空的情况:
        //即: 在if中肯定为true
        else
        {
            BstNode<Type> *tmp = currentNode;
            //如果左子女为空
            if (currentNode->leftChild == NULL)
                //则用他的右子女节点顶替他的位置
                currentNode = currentNode->rightChild;
            //如果右子女为空
            else
                //则用他的左子女节点顶替他的位置
                currentNode = currentNode->leftChild;
            //释放节点
            delete tmp;
        }
    }
}

二叉查找树的几个实用操作

//清空二叉树
template <typename Type>
void BsTree<Type>::makeEmpty(BstNode<Type> *subTree)
{
    if (subTree != NULL)
    {
        if (subTree->leftChild != NULL)
            makeEmpty(subTree->leftChild);
        if (subTree->rightChild != NULL)
            makeEmpty(subTree->rightChild);

        delete subTree;
    }
}
//二叉查找树的层次遍历
template <typename Type>
void BsTree<Type>::levelOrder() const
{
    std::queue< BstNode<Type> * > queue;
    queue.push(root);

    while (!queue.empty())
    {
        BstNode<Type> *currentNode = queue.front();
        queue.pop();

        visit(currentNode);
        if (currentNode->leftChild != NULL)
            queue.push(currentNode->leftChild);
        if (currentNode->rightChild != NULL)
            queue.push(currentNode->rightChild);
    }
}

二叉排序树的性能分析

     对于每一棵特定的二叉排序树,均可按照平均查找长度的定义来求它的 ASL 值,显然,由值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同,甚至可能差别很大(如果二叉查找树退化成一条链表, 则其插入/删除/查找的性能都会退化为O(N))。

     但是在随机情况下, 二叉排序树的搜索, 插入, 删除操作的平均时间代价为O(logN);

目录
相关文章
|
29天前
|
算法
数据结构之博弈树搜索(深度优先搜索)
本文介绍了使用深度优先搜索(DFS)算法在二叉树中执行遍历及构建链表的过程。首先定义了二叉树节点`TreeNode`和链表节点`ListNode`的结构体。通过递归函数`dfs`实现了二叉树的深度优先遍历,按预序(根、左、右)输出节点值。接着,通过`buildLinkedList`函数根据DFS遍历的顺序构建了一个单链表,展示了如何将树结构转换为线性结构。最后,讨论了此算法的优点,如实现简单和内存效率高,同时也指出了潜在的内存管理问题,并分析了算法的时间复杂度。
47 0
|
2月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
115 64
|
22天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
43 5
|
1月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
75 16
|
29天前
|
算法
数据结构之文件系统模拟(树数据结构)
本文介绍了文件系统模拟及其核心概念,包括树状数据结构、节点结构、文件系统类和相关操作。通过构建虚拟环境,模拟文件的创建、删除、移动、搜索等操作,展示了文件系统的基本功能和性能。代码示例演示了这些操作的具体实现,包括文件和目录的创建、移动和删除。文章还讨论了该算法的优势和局限性,如灵活性高但节点移除效率低等问题。
45 0
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
30 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
2月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
2月前
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解(三)
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解
|
2月前
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解(二)
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解
|
2月前
|
存储
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解(一)
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解