二叉查找树

Binary Search Tree Link

二叉树搜索树

//二叉查找树:又名二叉树搜索树(BST),也称为二叉排序树(binary sort tree)
//基本概念及定义
    //其存在的意义在于实现快速查找,当然也支持快速插入和删除
//常见操作
    //a)插入操作
    //b)删除操作
        //a)要删除的节点左右子树都为空(叶子节点)
            //a.1)直接把这个节点删除,
            //a.2)指向该被删除节点的父节点的相应孩子指针设置为空
        //b)如果要删除的节点的左子树为空或者右子树为空(只有左子树或右子树)
            //b.1)把这个节点删除。
            //b.2)更新指向该被删除节点的父节点的相应孩子指针,让该指针指向要删除节点的非空的子节点即可  
        //c)要删除的节点左子树和右子树都不为空,这种情况最复杂
            //c.1)找到这个要删除的节点的左子树的最右下节点(也可以找到这个要删除节点的右子树的最左下节点)
            //c.2)将该节点的值替换到要删除的节点上。
            //c.3)接着把刚刚找到的那个最右下节点删除即可。
    //c)查找值最大和最小节点值的操作
    //d)找出中序遍历序列中当前节点的前序和后继节点
            //d.1)当前二叉查找的前驱节点,一定是比当前节点小的一系列节点中节点值最大
            //d.2)当前二叉查找树的后继节点一定是比当前节点值大的一系列节点中节点值最小的。
    //e)二叉查找树如何存储重复节点
            //e.1)扩充二叉树的每个节点:把每个节点扩充成链表或者动态数组,这样每个节点就可以存储多个key值相同的数据
            //e.2)插入数据是,如果遇到相同的数据,就将该数据当做大于当前已经存在的节点的数据来处理,放入当前
                    //已经存在的节点的RightChild(当做小于,放入当前已经存在的节点的LeftChild也可以)

节点的定义

template<typename T>      //T代表数据元素的类型
struct BinaryTreeNode
{
    T   data;
    BinaryTreeNode<T>* LeftChild;           //左子节点
    BinaryTreeNode<T>* RightChild;          //右子节点
};

二叉查找树的定义

//二叉树查找树的定义
template<typename T>
class BinarySearchTree
{
public:
    BinarySearchTree();
    ~BinarySearchTree();

private:
    void ReleaseNode(BinaryTreeNode<T>* pnode);

public:
    void inOrder();                                         //二叉树中序遍历代码(排序),方便测试时显示节点数据
    void InsertElem(const T& e);                            //插入元素,不可以指定插入位置,程序内部会自动确定插入位置
    BinaryTreeNode<T>* SearchElem(const T& e);              //查找某个节点
    BinaryTreeNode<T>* Non_recursion_search(const T& e);    //使用非递归方式实现查找
    void DeleteElem(const T& e);                            //删除某个节点
    BinaryTreeNode<T>* SearchMaxValuePoint();               //查找值最大节点
    BinaryTreeNode<T>* SearchMinValuePoint();               //查找值最小的节点

private:
    void inOrder(BinaryTreeNode<T>* tNode);
    void InsertElem(BinaryTreeNode<T>*& tNode, const T& e);                         //注意第一个参数类型
    BinaryTreeNode<T>* SearchElem(BinaryTreeNode<T>* tNode, const T& e);
    BinaryTreeNode<T>* Non_recursion_search(BinaryTreeNode<T>* tNode, const T& e);  //使用非递归实现查找
    void DeleteElem(BinaryTreeNode<T>*& tNode, const T& e);                         //第一个参数类型是引用
    BinaryTreeNode<T>* SearchMaxValuePoint(BinaryTreeNode<T>* tNode);
    BinaryTreeNode<T>* SearchMinValuePoint(BinaryTreeNode<T>* tNode);

public:
    BinaryTreeNode<T>* GetPriorPoint_IO(BinaryTreeNode<T>* findnode);               //找按中序遍历的二叉查找树中当前节点的前驱节点
    BinaryTreeNode<T>* GetNextPoint_IO(BinaryTreeNode<T>* findnode);                //找按中序遍历的二叉查找树中当前节点的后继节点

private:
    BinaryTreeNode<T>* root;
};

构造与析构

template<typename T>
BinarySearchTree<T>::BinarySearchTree()
{
    root = nullptr;
}

template<typename T>
BinarySearchTree<T>::~BinarySearchTree()
{
    ReleaseNode(root);
}

节点的释放

template<typename T>
void BinarySearchTree<T>::ReleaseNode(BinaryTreeNode<T>* pnode)
{
    if (pnode != nullptr)
    {
        ReleaseNode(pnode->LeftChild);
        ReleaseNode(pnode->RightChild);
    }
    delete pnode;
}

中序遍历

template<typename T>
void BinarySearchTree<T>::inOrder()      //二叉树中序遍历代码(排序),方便测试时显示节点数据
{
    inOrder(root);
}

template<typename T>
void BinarySearchTree<T>::inOrder(BinaryTreeNode<T>* tNode)
{
    if (tNode != nullptr)
    {
        inOrder(tNode->LeftChild);
        cout << tNode->data << " ";
        inOrder(tNode->RightChild);
    }
}

节点的插入

template<typename T>
void BinarySearchTree<T>::InsertElem(const T& e) //插入元素,不可以指定插入位置,程序内部会自动确定插入位置
{
    InsertElem(root, e);
}

template<typename T>
void BinarySearchTree<T>::InsertElem(BinaryTreeNode<T>*& tNode, const T& e)   //注意第一个参数类型
{
    if (tNode == nullptr)    //空树
    {
        tNode = new BinaryTreeNode<T>;
        tNode->data = e;
        tNode->LeftChild = nullptr;
        tNode->RightChild = nullptr;
    }
    if (e > tNode->data)
    {
        InsertElem(tNode->RightChild, e);
    }
    else if (e < tNode->data)
    {
        InsertElem(tNode->LeftChild, e);
    }
    else
    {
        //要插入的数据与当前树中某节点数据相同,则不允许插入
    }
}

查找

  • 节点的查找

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchElem(const T& e)       //查找某个节点
{
    return SearchElem(root, e);
}

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchElem(BinaryTreeNode<T>* tNode, const T& e)
{
    if (tNode == nullptr)
    {
        return nullptr;
    }
    if (tNode->data == e)
    {
        return tNode;
    }
    if (e < tNode->data)
    {
        return SearchElem(tNode->LeftChild, e);
    }
    else
    {
        return SearchElem(tNode->RightChild, e);
    }
}
  • 节点的查找(非递归)

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::Non_recursion_search(const T& e)     //使用非递归方式实现查找
{
    return Non_recursion_search(root, e);
}

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::Non_recursion_search(BinaryTreeNode<T>* tNode, const T& e)      //使用非递归实现查找
{
    if (tNode == nullptr)
    {
        return nullptr;
    }
    BinaryTreeNode<T>* tmpnode = tNode;
    while (tmpnode)
    {
        if (tmpnode->data == e)
        {
            return tmpnode;
        }
        if (tmpnode->data > e)
        {
            tmpnode = tmpnode->LeftChild;
        }
        else
        {
            tmpnode = tmpnode->RightChild;
        }
    }
    return nullptr;
}
  • 最小值查找

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchMinValuePoint()        //查找值最小的节点
{
    return SearchMinValuePoint(root);
}

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchMinValuePoint(BinaryTreeNode<T>* tNode)
{
    if (tNode == nullptr)        //空树
        return nullptr;
    BinaryTreeNode<T>* tmpnode = tNode;
    while (tmpnode->LeftChild != nullptr)
        tmpnode = tmpnode->LeftChild;
    return tmpnode;
}
  • 最大值查找

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchMaxValuePoint()        //查找值最大节点
{
    return SearchMaxValuePoint(root);
}

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::SearchMaxValuePoint(BinaryTreeNode<T>* tNode)
{
    if (tNode == nullptr)        //空树
        return nullptr;
    BinaryTreeNode<T>* tmpnode = tNode;
    while (tmpnode->RightChild != nullptr)
        tmpnode = tmpnode->RightChild;
    return tmpnode;
}

节点的删除

template<typename T>
void BinarySearchTree<T>::DeleteElem(const T& e)     //删除某个节点
{
    return DeleteElem(root, e);
}

template<typename T>
void BinarySearchTree<T>::DeleteElem(BinaryTreeNode<T>*& tNode, const T& e)   //第一个参数类型是引用
{
    if (tNode == nullptr)
    {
        return;
    }
    if (e > tNode->data)
    {
        DeleteElem(tNode->RightChild, e);
    }
    else if (e < tNode->data)
    {
        DeleteElem(tNode->LeftChild, e);
    }
    else
    {
        //找到了节点执行删除操作
        if (tNode->LeftChild == nullptr && tNode->RightChild == nullptr) //要删除的节点左右子树都为空
        {
            //即将被删除的节点的左右孩子都为空
            BinaryTreeNode<T>* tmpnode = tNode;
            tNode = nullptr;       //这个实际上就是让指向该节点的父节点指向空
            delete tmpnode;
        }
        else if (tNode->LeftChild == nullptr)        //其实这个else if 可以和上个if代码合并
        {
            //即将被删除的节点左孩子为空(右孩子不为空)
            BinaryTreeNode<T>* tmpnode = tNode;
            tNode = tNode->RightChild;
            delete tmpnode;
        }
        else if (tNode->RightChild == nullptr)
        {
            //即将被删除的节点的右孩子为空(左孩子不为空)
            BinaryTreeNode<T>* tmpnode = tNode;
            tNode = tNode->LeftChild;
            delete tmpnode;
        }

        else
        {
            //即将被删除的节点的左右孩子都不为空
            //(1)找到这个要删除节点的左子树的最右下节点
            BinaryTreeNode<T>* tmpparentnode = tNode;    //tmoparentnode代表要删除节点的父节点
            BinaryTreeNode<T>* tmpnode = tNode->LeftChild;    //保存要删除节点左子树的最右下节点
            while (tmpnode->RightChild)
            {
                tmpparentnode = tmpnode;
                tmpnode = tmpnode->RightChild;
            }//end while

            tNode->data = tmpnode->data;
            //此时,tmpnode指向要删除节点左子树的最右下节点(即也就是真要删除的节点)
            //tmpparentnode指向真正要删除的节点的父节点

            //(2)删除tmpnode所指向的节点
            if (tmpparentnode == tNode)          //此条件成立表示上面while循环一次都没执行,也就意味着要删除节点的左孩子没有右孩子
            {
                tNode->LeftChild = tmpnode->LeftChild;  //让要删除节点的左孩子,指向真正要删除节点的左孩子的左孩子
            }
            else
            {
                //此条件成立,表示上面while循环至少执行一次,这意味着要删除节点的左子树有1到多个右孩子
                    //但右孩子不可能在有右孩子【因为tmpnode指向的是最后一个右孩子】
                tmpparentnode->RightChild = tmpnode->LeftChild;
            }
            //(3)把最右下节点删除
            delete tmpnode;
        }//end if

    }
}

按中序遍历查找

  • 找当前节点的前驱节点

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::GetPriorPoint_IO(BinaryTreeNode<T>* findnode)   //找按中序遍历的二叉查找树中当前节点的前驱节点
{
    if (findnode == nullptr)
        return nullptr;
    BinaryTreeNode<T>* prevnode = nullptr;
    BinaryTreeNode<T>* currnode = root;         //从当前节点开始找
    while (currnode != nullptr)
    {
        if (currnode->data < findnode->data)    //
        {
            //(1)从一系列比当前要找的值小的节点中找一个值最大的当前驱节点
            //当前节点值比要找的节点值小,所以当前节点认为有可能是前驱
            if (prevnode == nullptr)
            {
                //如果前驱节点还为空,不妨把当前节点认为就是前驱节点
                prevnode = currnode;
            }
            else   //prevnode 不为空
            {
                //既然是找前驱,那自然是找到比要找的值小的一系列节点中值最大的
                if (prevnode->data < currnode->data)
                {
                    prevnode = currnode;            //前驱节点自然是找一堆,比当前值小的值中最大的一个
                }
            }
            //(2)继续逼近要找的节点,一直到找到要找的节点,找到要找的节点后,要找的节点仍旧可能是前驱
            currnode = currnode->RightChild;  //当前节点小,所以往当前节点的右子树转。
        }
        else if (currnode->data > findnode->data)    //当前节点值必要找的值大,所以当前节点肯定不会是要找的值
        {
            //当前节点大,所以往当前节点的左子树转
            currnode = currnode->LeftChild;
        }
        else // if(currnode->data = findnode->data)
        {
            //当前节点值就是要找的节点值,那么前驱也可能在当前节点的左子树中,所以往左子树转,继续找看有没有更合适的前驱
            currnode = currnode->LeftChild;
        }//end if
    }//end while
    return prevnode;
}
  • 找当前节点的后继节点

template<typename T>
BinaryTreeNode<T>* BinarySearchTree<T>::GetNextPoint_IO(BinaryTreeNode<T>* findnode)     //找按中序遍历的二叉查找树中当前节点的后继节点
{
    if (findnode == nullptr)
    {
        return nullptr;
    }
    BinaryTreeNode<T>* nextnode = nullptr;
    BinaryTreeNode<T>* currnode = root;     //当前节点,从根开始找

    while (currnode != nullptr)
    {
        if (currnode->data > findnode->data)  //当前节点大
        {
            //(1)从一系列比当前要找的值大的节点中找一个值最小当后继节点
            //当前节点值比要找的节点值大,所以当前节点认为有可能就是后继
            if (nextnode == nullptr)
            {
                //如果后继节点还为空,那不妨就把当前节点认为就是后继
                nextnode = currnode;
            }
            else  //nextnode不为空
            {
                //既然是找后继,那自然是找到比要找的值大的一系列节点中值最小的
                if (nextnode->data > currnode->data)
                {
                    nextnode = currnode;  //后继自然是找一堆 比当前值大的,值中,最小的一个
                }
            }
            //(2)继续逼近要找的节点,一直找到要找的节点,找到要找的节点后,要找的节点的右节点仍旧可能是后继
            currnode = currnode->LeftChild;   //当前节点大,所以往当前节点的左子树转
        }
        else if (currnode->data < findnode->data)   //当前节点值比要找的值小,所以当前节点肯定不会是要找的值的后继
        {
            //当前节点小,所以往当前节点的右子树转
            currnode = currnode->RightChild;
        }
        else  //if(currnode->data = findnode->data)
        {
            //当前节点值就是要找的节点值,那么后继也可能在当前节点的右子树中,所以往右子树转,继续找,看看有没有更合适的
            currnode = currnode->RightChild;
        }  //end if
    }//end while
    return nextnode;
}

测试用例

int main()
{
    BinarySearchTree<int> tree;
    int myarray[] = { 23, 17, 11, 19, 8, 12 };
    int acount = sizeof(myarray) / sizeof(int);
    for (int i = 0; i < acount; ++i)
    {
        tree.InsertElem(myarray[i]);
    }
    tree.inOrder();
    cout << endl;

    int tmp = 19;
    BinaryTreeNode<int>* tmpp = tree.Non_recursion_search(tmp);
    if (tmpp != nullptr)
    {
        cout << "找到了值为: " << tmpp->data << " 的节点" << endl;
    }
    else
    {
        cout << "没找到值为: " << tmpp << " 的节点" << endl;

    }

    tree.DeleteElem(tmp);
    tree.inOrder();
    cout << endl;


    cout << "值最小的节点: " << tree.SearchMinValuePoint()->data << endl;
    cout << "值最大的节点: " << tree.SearchMaxValuePoint()->data << endl;

    return 0;
}

 

posted @ 2022-08-03 15:59  huahuati  阅读(28)  评论(0)    收藏  举报