C++数据结构之二叉查找树(BST)

二分查找法在算法家族大类中属于“分治法”,二分查找的过程比较简单,代码见我的另一篇日志,戳这里!因二分查找所涉及的有序表是一个向量,若有插入和删除结点的操作,则维护表的有序性所花的代价是O(n)。

就查找性能而言,二叉查找树和二分查找不分伯仲,但是,就维护表的有序性而言,二叉排序树无须移动结点,只需修改指针即可完成插入和删除操作,且其平均的执行时间均为O(lgn),因此更有效。二叉查找树,顾名思义,是一种可以用来二分查找的树数据结构,其左孩子比父节点小,右孩子比父节点大,还有一个特性就是”中序遍历“可以让结点有序。在对关键字进行查找的时候,根据关键字与根节点的关键字比较的结果,分别选择继续与其左子树或者右子树进行比较,直到找到所查找的关键字或者访问节点的左右子树不存在(没找到关键字)则退出!

二叉查找树的主要操作有:插入关键字查找关键字删除关键字。下面分别对这三个步骤做详细描述和算法实现。

为了方便,我将二叉查找树实现为一个类,结构如下:

typedef struct Node_
{
    struct Node_ *parent;
    struct Node_ *left;
    struct Node_ *right;
    T data;
}Node;

class BinaryTree
{
public:
    BinaryTree():root(NULL){};
    ~BinaryTree();
    bool insertNode(T data);
    bool deleteNode(T data);
    Node* findNode(T data);
private:
    Node *root;
};

1. 插入关键字的过程如下所示:

《C++数据结构之二叉查找树(BST)》

插入过程的代码如下:

bool BinaryTree::insertNode(T data)
{
    Node *x = NULL, *current, *parent;

    /***********************************************
    *  allocate node for data and insert in tree  *
    ***********************************************/

    /* find x's parent */
    current = root;
    parent = NULL;
    while (current) {
        if (compEQ(data, current->data)) return true;
        parent = current;
        current = compLT(data, current->data) ? 
            current->left : current->right;
    }

    /* setup new node */
    if ((x = (Node*)malloc(sizeof(*x))) == 0)
    {
        cout << "Insufficient memory (insertNode)!" << endl;
        return false;
    }
    x->data = data;
    x->parent = parent;
    x->left = NULL;
    x->right = NULL;

    /* insert x in tree*/
    if(parent)
        if(compLT(x->data, parent->data))
            parent->left = x;
        else
            parent->right = x;
    else
        root = x;

    return true;
}

其中的compEQ和compLT为2个宏定义,用来比较两个关键字的大小。

2. 查找关键字的过程比较简单,和二分查找方法类似,代码如下:

Node* BinaryTree::findNode(T data)
{
    /*******************************
    *  find node containing data  *
    *******************************/

    Node *current = root;
    while(current != NULL)
        if(compEQ(data, current->data))
            return current;
        else
            current = compLT(data, current->data) ? 
                current->left : current->right;
    return NULL;
}

3. 删除关键字的过程分为2种情况讨论:单孩子的情况和左右孩子的情况。

1> 单孩子情况分析:

如果删除的节点有左孩子那就把左孩子顶上去,如果有右孩子就把右孩子顶上去,so easy!如图所示:

《C++数据结构之二叉查找树(BST)》

2> 左右孩子情况分析:

首先可以这么想象,如果我们要删除一个数组的元素,那么我们在删除后会将其后面的一个元素顶到被删除的位置,如下图所示:

《C++数据结构之二叉查找树(BST)》

那么二叉树操作同样也是一样,我们根据“中序遍历(inorder tree walk)”找到要删除结点的后一个结点,然后顶上去就行了,原理跟“数组”一样一样的。

《C++数据结构之二叉查找树(BST)》

好了,贴代码:

bool BinaryTree::deleteNode(T data)
{
    Node* pNode = findNode(data);
    if (pNode == NULL)
    {
        cout << "Cannot find this data in BST!" << endl;
        return false;
    }

    Node *x, *y;
    /* find tree successor */
    if (pNode->left == NULL || pNode->right == NULL)
        y = pNode;
    else {
        y = pNode->right;
        while (y->left != NULL) y = y->left;
    }

    /* x is y's only child */
    if (y->left != NULL)
        x = y->left;
    else
        x = y->right;

    /* remove y from the parent chain */
    if (x) x->parent = y->parent;
    if (y->parent)
        if (y == y->parent->left)
            y->parent->left = x;
        else
            y->parent->right = x;
    else
        root = x;

    /* y is the node we're removing */
    /* z is the data we're removing */
    /* if z and y are not the same, replace z with y. */
    if (y != pNode) {
        y->left = pNode->left;
        if (y->left) y->left->parent = y;
        y->right = pNode->right;
        if (y->right) y->right->parent = y;
        y->parent = pNode->parent;
        if (pNode->parent)
            if (pNode == pNode->parent->left)
                pNode->parent->left = y;
            else
                pNode->parent->right = y;
        else
            root = y;
        free (pNode);
    }
    else {
        free (y);
    }
    return true;
}

好了,二叉查找树的代码告一段落,我们在来分析一下二叉查找树的插入过程,假如有以下序列:<4, 17, 16, 20, 37, 38, 43>,则会生成如下所示二叉树:

《C++数据结构之二叉查找树(BST)》

这已经完全退化成了一个单链表,势必影响到关键字的查找过程。不过总会有解决办法的,下一篇博客我将继续这个话题,对普通二叉树经过旋转,即使用平衡二叉树,使其保持最坏复杂度在O(logN)。

谢谢大家的阅读,希望能够帮到大家!PS:文章中部分图片利用了博客园另外一篇文章的插图(戳这里)!

Published by Windows Live Write!

    原文作者:薛定谔の喵
    原文地址: https://www.cnblogs.com/berlin-sun/p/BinarySearchTree.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞