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