旭说数据结构之二叉查找树

上一篇详细介绍树的特殊结构——二叉树的相关知识和操作。
这一篇介绍二叉树的特殊结构——二叉查找树。
较之一般的二叉树,二叉查找树限定了结点的左子树的所有结点值均小于该结点,结点的右子树的所有结点值均大于该结点。下图给了一个示意:
这里写图片描述
所以在往二叉查找树中插入元素时,要注意,不能随便找到一个空位就插入,而是要通过与根结点的比较,判断插在根结点的左子树还有右子树,倘若应该插在右子树,在与右子树的根结点比较,判断应该插在右子树的哪个子树上,如此,这是个递归的过程。于是我们可以用递归来实现插入:

1.递归和非递归插入

    //递归插入数据
    void insertDataToBST1(BSTNode<DataType>**root,DataType data)
    {
        if (*root == NULL)
        {
            *root = new BSTNode<DataType>(data);
            return;
        }
        if (data<((*root)->_data))
        {
            insertDataToBST1(&((*root)->_lChild),data);
        }
        if (data>((*root)->_data))
        {
            insertDataToBST1(&((*root)->_rChild),data);
        }
    }

非递归插入数据的思路也不难,下面给出代码:

    //非递归插入数据
    void insertDataToBST2(BSTNode<DataType>**root,DataType data)
    {
        if (*root == NULL)
        {
            *root = new BSTNode<DataType>(data);
            return;
        }
        BSTNode<DataType>* p = *root;
        while(p){//从根结点开始,寻找到一个能放置data的空位置
            if (data > p->_data)//如果data比该结点大,而正好该结点的右孩子为NULL,
                                //则正好插入到这个结点的右孩子处
            {
                if (p->_rChild == NULL)
                {
                    p->_rChild = new BSTNode<DataType>(data);
                    break;
                }
                else//如果右孩子不是空的,则还得往下找
                {
                    p = p->_rChild;
                }
            }

            if (data < p->_data)
            {
                if (p->_lChild == NULL)
                {
                    p->_lChild = new BSTNode<DataType>(data);
                    break;
                }
                else
                {
                    p=p->_lChild;
                }
            }
        }
    }

2.遍历

由于二叉查找树的特殊性质,我们对一颗二叉查找树进行中序遍历,得到的中序序列是按照从小到大的顺序排列的。
二叉查找树也是一颗二叉树,故遍历方法与二叉树的相同:

//递归中序遍历
    void inOrderWithRecursion()
    {
        cout<<"中序遍历";
        inOrder1(_root);
    }
    void inOrder1(BSTNode<DataType>* root)
    {
        if (root == NULL)return;
        inOrder1(root->_lChild);
        cout<<root->_data;
        inOrder1(root->_rChild);
    }

    //非递归中序遍历
    void inOrder()
    {
        Stack<BSTNode<DataType>*>* nodeStack = new Stack<BSTNode<DataType>*>;
        BSTNode<DataType>* p = _root;
        while(p!=NULL)// 从根结点一路向左,入栈
        {
            nodeStack->push(p);
            p=p->_lChild; 
        }
        //取出栈中元素,打印,发现该元素有右子树,把右子树一路向左入栈
        while(!nodeStack->isEmpty())
        {
            p = nodeStack->pop();
            cout<<p->_data;
            if (p->_rChild)
            {
                p=p->_rChild;
                while(p)
                {
                    nodeStack->push(p);
                    p=p->_lChild;
                }
            }
        }
    }

3.删除操作

删除操作是较复杂的一个操作,得分不同的情况进行讨论,下图给出了示意:
这里写图片描述
此外如果我们删除的结点是根结点,情况也有些许不同:
这里写图片描述

下面给出代码:代码中的getParentNode是一个成员函数,用于返回给定结点的父结点;findData也是一个成员函数,根据给定值返回对应的结点;findMin用于返回给定子树的最小值结点

bool deleteData(DataType data)
    {
        //首先在整个树中寻找是否有这个结点
        //findData是一个成员函数
        BSTNode<DataType>* p = findData(data);
        //如果没有找到这个结点,就不用删除了,返回false
        if (p==NULL)return false;

        //如果找到了要删除的结点,要判断这个结点是不是根结点

        if (p!=_root)//如果删除的不是根结点,则p定有父结点
        {
            if (p->_lChild==NULL&&p->_rChild==NULL)//要删除结点的左右孩子都为空
            {
                //如果要删除节点被父结点的左指针指着,则把这个左指针设为NULL
                if (getParentNode(p)->_lChild = p)
                {
                    getParentNode(p)->_lChild = NULL;
                }else{//如果要删除节点被父结点的右指针指着,则把这个右指针设为NULL
                    getParentNode(p)->_rChild = NULL;
                }
                delete p;
            }else if (p->_lChild && p->_rChild)//要删除结点的左右孩子都不为空
            {
                BSTNode<DataType>*minNode = findMin(p->_rChild);
                if (minNode == p->_rChild)
                {
                    p->_data = minNode->_data;
                    p->_rChild=minNode->_rChild;
                    delete minNode;
                }else{
                    p->_data = minNode->_data;
                    getParentNode(minNode)->_lChild = NULL;
                    delete minNode;
                }
            }else{//要删除结点只有一个孩子
                if (p->_rChild)//有右孩子的话
                {
                    if (getParentNode(p)->_lChild = p)
                    {
                        getParentNode(p)->_lChild = p->_rChild;
                    }else{
                        getParentNode(p)->_rChild = p->_rChild;
                    }
                }
                if (p->_lChild)//有左孩子的话
                {
                    if (getParentNode(p)->_lChild = p)
                    {
                        getParentNode(p)->_lChild = p->_lChild;
                    }else{
                        getParentNode(p)->_rChild = p->_lChild;
                    }
                }
                delete p;
            }
        }else{//如果要删除的是根结点
            if (p->_lChild==NULL&&p->_rChild==NULL)//如果根结点的左右孩子都为空
            {
                if (_root)
                {
                    delete _root;
                    _root =NULL;
                }
            }else if (p->_lChild&&p->_rChild){//如果根结点左右孩子都有
                BSTNode<DataType>*minNode = findMin(p->_rChild);
                if (minNode == p->_rChild)
                {
                    p->_data = minNode->_data;
                    p->_rChild=minNode->_rChild;
                    delete minNode;
                }else{
                    p->_data = minNode->_data;
                    getParentNode(minNode)->_lChild = NULL;
                    delete minNode;
                }
            }else{
                if (p->_lChild)//如果有左子树
                {
                    _root = p->_lChild;
                    delete p;
                }
                if (p->_rChild)
                {
                    _root = p->_rChild;
                    delete p;
                }
            }

        }
        return true;
    }

4.寻找包含关键字的结点

    BSTNode<DataType>* findData(DataType data)
    {
        BSTNode<DataType>* p = _root;
        while(p)
        {
            if (data > p->_data)
            {
                p = p->_rChild;
            }else if (data<p->_data)
            {
                p = p->_lChild;
            }else {
                break;
            }
        }
        //p可能为NULL
        if (p)
        {
            cout<<"找到了"<<p->_data<<endl;
        }else{
            cout<<"没有找到"<<data;
        }
        return p;
    }

5.寻找给定子树的的最小值

    BSTNode<DataType>* findMin(BSTNode<DataType>* node)
    {
        BSTNode<DataType>* p = node;
        while(p)
        {
            if (p->_lChild == NULL)
            {
                break;
            }
            p = p->_lChild;
        }
        return p;
    }

6. 找一个结点的父结点


    BSTNode<DataType>* getParentNode(BSTNode<DataType>* child)
    {
        if (child == NULL||child == _root)return NULL;
        //利用层序遍历的思路,使用队列
        Queue<BSTNode<DataType>*>* queue = new Queue<BSTNode<DataType>*>;
        //先把根结点入队
        queue->addDataToQueue(_root);
        BSTNode<DataType>* p = NULL;
        //当队列不空时
        while(queue->count()){
            //取出队首元素,第一次取出的是根结点,打印根结点,再把根结点的左右孩子入队列,
            //下一次取出的队首元素就是root的左孩子,然后把它的左右孩子入队列
            //每次都是打印这个结点,然后把它的左右孩子入队,最终所有的结点都会入队列,
            p=queue->deleteDataFromQueue();
            if (p->_lChild)
            {
                if (p->_lChild == child)
                {
                    return p;

                }
                queue->addDataToQueue(p->_lChild);
            }
            if (p->_rChild)
            {
                if (p->_rChild == child)
                {
                    return p;

                }
                queue->addDataToQueue(p->_rChild);
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值