您的位置:首页 > 理论基础 > 数据结构算法

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

2015-11-20 17:18 239 查看
上一篇详细介绍树的特殊结构——二叉树的相关知识和操作。

这一篇介绍二叉树的特殊结构——二叉查找树。

较之一般的二叉树,二叉查找树限定了结点的左子树的所有结点值均小于该结点,结点的右子树的所有结点值均大于该结点。下图给了一个示意:



所以在往二叉查找树中插入元素时,要注意,不能随便找到一个空位就插入,而是要通过与根结点的比较,判断插在根结点的左子树还有右子树,倘若应该插在右子树,在与右子树的根结点比较,判断应该插在右子树的哪个子树上,如此,这是个递归的过程。于是我们可以用递归来实现插入:

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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: