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

来源:互联网 发布:ubuntu 卸载apache2 编辑:程序博客网 时间:2024/04/29 00:59

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

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