您的位置:首页 > 其它

《算法导论》读书笔记之第12章 二叉查找树

2013-01-28 21:49 316 查看
摘要:

  本章介绍了二叉查找树的概念及操作。主要内容包括二叉查找树的性质,如何在二叉查找树中查找最大值、最小值和给定的值,如何找出某一个元素的前驱和后继,如何在二叉查找树中进行插入和删除操作。在二叉查找树上执行这些基本操作的时间与树的高度成正比,一棵随机构造的二叉查找树的期望高度为O(lgn),从而基本动态集合的操作平均时间为θ(lgn)。

1、二叉查找树

  二叉查找树是按照二叉树结构来组织的,因此可以用二叉链表结构表示。二叉查找树中的关键字的存储方式满足的特征是:设x为二叉查找树中的一个结点。如果y是x的左子树中的一个结点,则key[y]≤key[x]。如果y是x的右子树中的一个结点,则key[x]≤key[y]。根据二叉查找树的特征可知,采用中根遍历一棵二叉查找树,可以得到树中关键字有小到大的序列。/article/4834198.html介绍了二叉树概念及其遍历。一棵二叉树查找及其中根遍历结果如下图所示:

View Code

#include <iostream>
#include <stack>
#include <cstdlib>
using namespace std;

template <class T>
class  BinarySearchTreeNode
{
public:
T elem;
struct BinarySearchTreeNode<T> *parent;
struct BinarySearchTreeNode<T>* left;
struct BinarySearchTreeNode<T>* right;
};

template <class T>
class BinarySearchTree
{
public:
BinarySearchTree();
void tree_insert(const T& elem);
int  tree_remove(const T& elem );
BinarySearchTreeNode<T>* tree_search(const T& elem)const;
T tree_minmum(BinarySearchTreeNode<T>* root)const;
T tree_maxmum(BinarySearchTreeNode<T>* root)const;
T tree_successor(const T& elem) const;
T tree_predecessor(const T& elem)const;
int empty() const;
void inorder_tree_walk()const;
BinarySearchTreeNode<T>* get_root()const {return root;}
private:
BinarySearchTreeNode<T>* root;
};

template <class T>
BinarySearchTree<T>::BinarySearchTree()
{
root = NULL;
}

template <class T>
void BinarySearchTree<T>::tree_insert(const T& elem)
{
if(!empty())
{
BinarySearchTreeNode<T> *pnode = root;
BinarySearchTreeNode<T> *qnode = NULL;
BinarySearchTreeNode<T> *newnode = new BinarySearchTreeNode<T>;
newnode->elem = elem;
newnode->parent = NULL;
newnode->left = NULL;
newnode->right = NULL;
while(pnode)
{
qnode = pnode;
if(pnode->elem > elem)
pnode = pnode->left;
else
pnode = pnode->right;
}
if(qnode->elem > elem)
qnode->left = newnode;
else
qnode->right = newnode;
newnode->parent = qnode;
}
else
{
root = new BinarySearchTreeNode<T>;
root->elem = elem;
root->parent =NULL;
root->left = NULL;
root->right = NULL;
}
}

template <class T>
int BinarySearchTree<T>::tree_remove(const T&elem)
{
BinarySearchTreeNode<T> *pnode;
BinarySearchTreeNode<T> *parentnode,*snode;
pnode = tree_search(elem);
if(pnode != NULL)
{
parentnode = pnode->parent;
if(pnode->right == NULL || pnode->left == NULL)
{
if(pnode->right != NULL)
{
if(parentnode->left == pnode)
parentnode->left = pnode->right;
if(parentnode->right == pnode)
parentnode->right = pnode->right;
pnode->right->parent = parentnode;
}
else if(pnode->left != NULL)
{
if(parentnode->left == pnode)
parentnode->left = pnode->left;
if(parentnode->right == pnode)
parentnode->right = pnode->left;
pnode->left->parent = parentnode;
}
else
{
if(parentnode->left == pnode)
parentnode->left = NULL;
if(parentnode->right == pnode)
parentnode->right = NULL;
}
delete pnode;
}
else
{
snode = tree_search(tree_successor(pnode->elem));
pnode->elem = snode->elem;
if(snode->parent->left == snode)
{
snode->parent->left = snode->right;
snode->right->parent = snode->parent->left;
}
if(snode->parent->right == snode)
{
snode->parent->right = snode->right;
snode->right->parent = snode->parent->right;
}
delete snode;
}
return 0;
}
return -1;
}
template <class T>
BinarySearchTreeNode<T>* BinarySearchTree<T>::tree_search(const T& elem)const
{
BinarySearchTreeNode<T> *pnode = root;
while(pnode)
{
if(pnode->elem == elem)
break;
else if(pnode->elem > elem)
pnode = pnode->left;
else
pnode = pnode->right;
}
return pnode;
}

template <class T>
T BinarySearchTree<T>::tree_minmum(BinarySearchTreeNode<T>* root)const
{
BinarySearchTreeNode<T> *pnode = root;
if(pnode->left)
{
while(pnode->left)
pnode = pnode->left;
}
return pnode->elem;
}

template <class T>
T BinarySearchTree<T>::tree_maxmum(BinarySearchTreeNode<T>* root)const
{
BinarySearchTreeNode<T> *pnode = root;
if(pnode->right)
{
while(pnode->right)
pnode = pnode->right;
}
return pnode->elem;
}

template <class T>
T BinarySearchTree<T>::tree_successor(const T& elem) const
{
BinarySearchTreeNode<T>* pnode = tree_search(elem);
BinarySearchTreeNode<T>* parentnode;
if(pnode != NULL)
{
if(pnode->right)
return tree_minmum(pnode->right);
parentnode = pnode->parent;
while(parentnode && pnode == parentnode->right)
{
pnode = parentnode;
parentnode = parentnode->parent;
}
if(parentnode)
return parentnode->elem;
else
return T();
}
return T();
}
template <class T>
T BinarySearchTree<T>::tree_predecessor(const T& elem)const
{
BinarySearchTreeNode<T>* pnode = tree_search(elem);
BinarySearchTreeNode<T>* parentnode;
if(pnode != NULL)
{
if(pnode->right)
return tree_maxmum(pnode->right);
parentnode = pnode->parent;
while(parentnode && pnode == parentnode->left)
{
pnode = parentnode;
parentnode = pnode->parent;
}
if(parentnode)
return parentnode->elem;
else
return T();
}
return T();
}

template <class T>
int BinarySearchTree<T>::empty() const
{
return (NULL == root);
}

template <class T>
void BinarySearchTree<T>::inorder_tree_walk()const
{
if(NULL != root)
{
stack<BinarySearchTreeNode<T>*> s;
BinarySearchTreeNode<T> *ptmpnode;
ptmpnode = root;
while(NULL != ptmpnode || !s.empty())
{
if(NULL != ptmpnode)
{
s.push(ptmpnode);
ptmpnode = ptmpnode->left;
}
else
{
ptmpnode = s.top();
s.pop();
cout<<ptmpnode->elem<<" ";
ptmpnode = ptmpnode->right;
}
}
}
}
int main()
{
BinarySearchTree<int> bstree;
BinarySearchTreeNode<int>* ptnode,*proot;
bstree.tree_insert(32);
bstree.tree_insert(21);
bstree.tree_insert(46);
bstree.tree_insert(54);
bstree.tree_insert(16);
bstree.tree_insert(38);
bstree.tree_insert(70);
cout<<"inorder tree walk is: ";
bstree.inorder_tree_walk();
proot = bstree.get_root();
cout<<"\nmax value is: "<<bstree.tree_maxmum(proot)<<endl;
cout<<"min value is: "<<bstree.tree_minmum(proot)<<endl;
ptnode = bstree.tree_search(38);
if(ptnode)
cout<<"the element 38 is exist in the binary tree.\n";
else
cout<<"the element 38 is not exist in the binary tree.\n";
cout<<"the successor of 38 is: "<<bstree.tree_successor(38)<<endl;
cout<<"the predecessor of 38 is:"<<bstree.tree_predecessor(38)<<endl;
if(bstree.tree_remove(46) == 0)
cout<<"delete 46 successfully"<<endl;
else
cout<<"delete 46 failed"<<endl;
cout<<"inorder tree walk is: ";
bstree.inorder_tree_walk();
exit(0);
}


程序测试结果如下所示:



  二叉树实现时候添加了一个父结点指针,方便寻找给定结点的前驱和后继。二叉树中删除操作实现比较复杂,需要分类讨论,我分三种情况进行讨论,程序写的有些繁琐,可以进行优化。优化后的代码如下:

template <class T>
int BinarySearchTree<T>::tree_delete(const T& elem)
{
//找到该元素对应的结点
BinarySearchTreeNode<T>* pnode = tree_search(elem);
if(NULL != pnode)
{
BinarySearchTreeNode<T> *qnode,*tnode;
//判断结点是否有左右孩子
if(pnode->left == NULL || pnode->right == NULL)
qnode = pnode;   //有一个左孩子或者一个右孩子和没有左右孩子
else
qnode = tree_search(tree_successor(elem)); //有左右孩子
if(NULL != qnode->left)
tnode = qnode->left;
else
tnode = qnode->right;
if(NULL != tnode)
tnode->parent = qnode->parent;
if(qnode->parent == NULL)
root = tnode;
else
if(qnode == qnode->parent->left)
qnode->parent->left = tnode;
else
qnode->parent->right = tnode;
if(qnode != pnode)
pnode->elem = qnode->elem;  //将后继结点的值复制到要删除的结点的值
delete qnode;
return 0;
}
return -1;
}


5、随机构造二叉查找树

  二叉查找上各种基本操作的运行时间都是O(h),h为树的高度。但是在元素插入和删除过程中,树的高度会发生改变。如果n个元素按照严格增长的顺序插入,那个构造出的二叉查找树的高度为n-1。例如按照先后顺序插入7、15、18、20、34、46、59元素构造二叉查找树,二叉查找树结构如下所示:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: