二叉查詢樹【資料結構】
二叉查詢樹
一、介紹
二叉查詢樹(Binary Search Tree),也被稱作二叉搜尋樹。設x是二叉查詢樹中的任意一個結點,則x的鍵值大於等於它的左子樹中任意一個結點的鍵值,小於等於它的右子樹中任意一個結點的鍵值。
二、原理
1、結點的前驅和後繼
結點的前驅:二叉樹中鍵值小於該結點的最大結點。
結點的後繼:二叉樹中鍵值大於該結點的最小結點
查詢前驅結點的方法:
如果x存在左孩子,則x的前驅結點為以其左孩子為根的子樹的最大結點
如果x不存在左孩子:
1>x是一個右孩子,前驅結點就是它的父結點
2>x是一個左孩子,查詢x的最低父結點,並且x位於該父結點的右子樹中,前驅結點就是這個父結點
查詢後繼結點的方法
如果x存在右孩子,則x的後繼結點為以其右孩子為根的子樹的最小結點
如果x不存在右孩子:
1>x是一個左孩子,後繼結點就是它的父結點
2>x是一個右孩子,查詢x的最低父結點,並且x位於該父結點的左子樹中,後繼結點就是這個父結點
2、插入結點
插入操作均是在葉結點處進行的,因此,只要按照插入結點的值找到相應的葉結點就OK了,程式中x、y表示兩個結點,x初始化為根結點,通過這兩個變數尋找新結點 z 的插入位置,根據x結點的值與z結點的值的比較結果選擇對應的路徑,直到x等於NULL,此時y就是我們想尋找的葉節點,這時只需將y和z連結起來就行。
//插入鍵值為key的結點(內部介面) template <class T> void BSTree<T>::insert(Node<T>* &tree,Node<T> *z) { Node<T> *y=NULL; Node<T> *x=tree; //尋找z的插入位置 while(x) { y=x; if(z->key<x->key) x=x->l; else x=x->r; } if(!y)//樹為空 tree=z; else { z->p=y;//z的父結點為y if(z->key<y->key)//z是y的左孩子 y->l=z; else y->r=z;//z是y的右孩子 } } //插入鍵值為key的結點(外部介面) template<class T> void BSTree<T>::insert(T key) { Node<T> *z=new Node<T>(key,NULL,NULL,NULL); if(z) insert(root,z); }
3、刪除結點
根據鍵值進行刪除操作,刪除結點後還要維持二叉查詢樹的特點,真正被刪除的結點其實是這一類結點:最多隻有一個孩子結點,刪除這類結點非常簡單,只需要將它的孩子結點和它的父結點進行連結,再把自己釋放掉就行。當被刪除結點就是這類結點時,直接按上面的操作進行即可。當被刪除結點不是這類結點時,就需要找到一個代替的點,這就是該結點的後繼結點,將該結點的鍵值變為後繼結點的鍵值,然後刪除後繼結點即可,容易知道後繼結點是上面描述的那類結點,因此照著描述進行操作即可。
//刪除結點(內部介面)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
Node<T> *x=NULL,*y=NULL;//y是真正要被刪除的結點,x是y的孩子結點
if(z->l&&z->r)//如果z的左右孩子都在,y就等於z的後繼結點
y=successor(z);
else//否則,y=z
y=z;
//此時對於 y有3種情況 1.只存在左孩子 2.只存在右孩子 3.不存在孩子
if(y->l)//情況一
x=y->l;
else //情況二、三
x=y->r;
if(x) x->p=y->p;//如果y存在孩子結點,那麼就將孩子結點的父結點變為y的父結點
if(!y->p)
tree=x;//如果y是根結點,就讓根結點變為x
else if(y==y->p->l)
y->p->l=x;
else
y->p->r=x;
if(y!=z)
z->key=y->key;
return y;
}
//刪除結點,並返回結點(外部介面)
template<class T>
void BSTree<T>::remove(T key)
{
Node<T> *z,*node;
if((z=search(root,key)))//查詢到鍵值為key的結點
{
if((node=remove(root,z)))//刪除成功
delete node;
}
}
三、C++實現
注意:程式使用了內部介面和外部介面,這樣做主要是因為資料成員 root 為私有變數
#include<iostream>
using namespace std;
template<class T>
class Node{
public:
T key;//鍵值
Node *p;//父節點
Node *l;//左孩子
Node *r;//右孩子
Node(T key,Node *p,Node *l,Node *r):key(key),p(p),l(l),r(r){}
};
template<class T>
class BSTree{
private:
Node<T> *root;//根結點
public:
BSTree();
~BSTree();
void preOrder();//前序遍歷
void inOrder();//中序遍歷
void postOrder(); //後序遍歷
Node<T>* search(T key); //查詢二叉樹中鍵值為key的結點 (遞迴實現)
Node<T>* iterativeSearch(T key); //查詢二叉樹中鍵值為key的結點(非遞迴實現)
T minimum();//查詢最小結點:返回最小結點的鍵值
T maximum();//查詢最大結點:返回最大結點的鍵值
//查詢結點x的前驅結點,即查詢二叉樹中鍵值小於該結點的最大結點
Node<T>* predecessor(Node<T> *x);
//查詢結點x的後繼結點,即查詢二叉樹中鍵值大於該結點的最小結點
Node<T>* successor(Node<T> *x);
void insert(T key);//將鍵值為key的結點插入到二叉樹中
void remove(T key);//刪除鍵值為key的結點
void destroy();//銷燬二叉樹
void print();//列印二叉樹
private:
void preOrder(Node<T> *tree);//前序遍歷
void inOrder(Node<T> *tree);//中序遍歷
void postOrder(Node<T> *tree);//後序遍歷
Node<T>* search(Node<T>* x,T key);//查詢鍵值為key的結點(遞迴實現)
Node<T>* iterativeSearch(Node<T>* x,T key);//查詢鍵值為key的結點(非遞迴實現)
//查詢最小結點:返回tree為根結點的二叉樹的最小結點
Node<T>* minimum(Node<T>* tree);
//查詢最大結點:返回tree為根節點的二叉樹的最大結點
Node<T>* maximum(Node<T>* tree);
void insert(Node<T>* &tree,Node<T> *z);//插入結點z
Node<T>* remove(Node<T>* &tree,Node<T> *z);//刪除並返回結點z
void destroy(Node<T>* &tree);//銷燬二叉樹
void print(Node<T>* tree,T key,int direction);//列印二叉樹
};
//建構函式
template<class T>
BSTree<T>::BSTree()
{
root=NULL;
}
//解構函式
template<class T>
BSTree<T>::~BSTree()
{
destroy();
}
//前序遍歷(內部介面)
template<class T>
void BSTree<T>::preOrder(Node<T>* tree)
{
if(tree)
{
cout<<tree->key<<" ";
preOrder(tree->l);
preOrder(tree->r);
}
}
//前序遍歷(外部介面)
template<class T>
void BSTree<T>::preOrder()
{
preOrder(root);
}
//中序遍歷(內部介面)
template<class T>
void BSTree<T>::inOrder(Node<T>* tree)
{
if(tree)
{
inOrder(tree->l);
cout<<tree->key<<" ";
inOrder(tree->r);
}
}
//中序遍歷(外部介面)
template<class T>
void BSTree<T>::inOrder()
{
inOrder(root);
}
//後序遍歷(內部介面)
template<class T>
void BSTree<T>::postOrder(Node<T> *tree)
{
if(tree)
{
postOrder(tree->l);
postOrder(tree->r);
cout<<tree->key<<" ";
}
}
//後序遍歷(外部介面)
template<class T>
void BSTree<T>::postOrder()
{
postOrder(root);
}
//查詢鍵值為key的結點(遞迴實現) 內部介面
template<class T>
Node<T>* BSTree<T>::search(Node<T>* x,T key)
{
if(!x||x->key==key)
return x;
if(key<x->key)
return search(x->l,key);
else
return search(x->r,key);
}
//查詢鍵值為key的結點(遞迴實現) 外部介面
template<class T>
Node<T>* BSTree<T>::search(T key)
{
search(root,key);
}
//查詢鍵值為key的結點(非遞迴實現) 內部介面
template<class T>
Node<T>* BSTree<T>::iterativeSearch(Node<T>* x,T key)
{
while(x)
{
if(key==x->key)
return x;
else if(key<x->key)
x=x->l;
else
x=x->r;
}
return x;
}
//查詢鍵值為key的結點(非遞迴實現) 外部介面
template<class T>
Node<T>* BSTree<T>::iterativeSearch(T key)
{
iterativeSearch(root,key);
}
//查詢最大值(內部介面)
template<class T>
Node<T>* BSTree<T>::maximum(Node<T>* tree)
{
if(!tree) return NULL;
while(tree->r) tree=tree->r;
return tree;
}
//查詢最大值(外部介面)
template<class T>
T BSTree<T>::maximum()
{
Node<T>* p=maximum(root);
if(p)
return p->key;
else
return (T)NULL;
}
//查詢最小值(內部介面)
template<class T>
Node<T>* BSTree<T>::minimum(Node<T>* tree)
{
if(!tree) return NULL;
while(tree->l) tree=tree->l;
return tree;
}
//查詢最小值(外部介面)
template<class T>
T BSTree<T>::minimum()
{
Node<T>* p=minimum(root);
if(p)
return p->key;
else
return (T)NULL;
}
//結點的前驅:該結點的左子樹的最大結點
//結點的後繼:該結點的右子樹的最小結點
//查詢結點的前驅結點,即二叉樹中鍵值小於該結點的最大結點
/*
如果x存在左孩子,則x的前驅結點為以其左孩子為根的子樹的最大結點
如果x不存在左孩子:
1>x是一個右孩子,前驅結點就是它的父結點
2>x是一個左孩子,查詢x的最低父結點,並且x位於該父結點的右子樹中,前驅結點就是這個父結點
*/
template<class T>
Node<T>* BSTree<T>::predecessor(Node<T> *x)
{
if(x->l) return maximum(x->l);
Node<T>* y=x->p;
while(y&&(x==y->l))
{
x=y;
y=y->p;
}
return y;
}
//查詢結點的後繼結點,即二叉樹中鍵值大於該結點的最小結點
/*
如果x存在右孩子,則x的後繼結點為以其右孩子為根的子樹的最小結點
如果x不存在右孩子:
1>x是一個左孩子,後繼結點就是它的父結點
2>x是一個右孩子,查詢x的最低父結點,並且x位於該父結點的左子樹中,後繼結點就是這個父結點
*/
template<class T>
Node<T>* BSTree<T>::successor(Node<T> *x)
{
if(x->r) return maximum(x->r);
Node<T> *y=x->p;
while(y&&(x==y->r))
{
x=y;
y=y->p;
}
return y;
}
//插入鍵值為key的結點(內部介面)
template <class T>
void BSTree<T>::insert(Node<T>* &tree,Node<T> *z)
{
Node<T> *y=NULL;
Node<T> *x=tree;
//尋找z的插入位置
while(x)
{
y=x;
if(z->key<x->key)
x=x->l;
else
x=x->r;
}
if(!y)//樹為空
tree=z;
else
{
z->p=y;//z的父結點為y
if(z->key<y->key)//z是y的左孩子
y->l=z;
else
y->r=z;//z是y的右孩子
}
}
//插入鍵值為key的結點(外部介面)
template<class T>
void BSTree<T>::insert(T key)
{
Node<T> *z=new Node<T>(key,NULL,NULL,NULL);
if(z) insert(root,z);
}
//刪除結點(內部介面)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
Node<T> *x=NULL,*y=NULL;//y是真正要被刪除的結點,x是y的孩子結點
if(z->l&&z->r)//如果z的左右孩子都在,y就等於z的後繼結點
y=successor(z);
else//否則,y=z
y=z;
//此時對於 y有3種情況 1.只存在左孩子 2.只存在右孩子 3.不存在孩子
if(y->l)//情況一
x=y->l;
else //情況二、三
x=y->r;
if(x) x->p=y->p;//如果y存在孩子結點,那麼就將孩子結點的父結點變為y的父結點
if(!y->p)
tree=x;//如果y是根結點,就讓根結點變為x
else if(y==y->p->l)
y->p->l=x;
else
y->p->r=x;
if(y!=z)
z->key=y->key;
return y;
}
//刪除結點,並返回結點(外部介面)
template<class T>
void BSTree<T>::remove(T key)
{
Node<T> *z,*node;
if((z=search(root,key)))//查詢到鍵值為key的結點
{
if((node=remove(root,z)))//刪除成功
delete node;
}
}
//列印(內部介面)
template<class T>
void BSTree<T>::print(Node<T> *tree,T key,int direction)
{
if(tree)
{
if(direction==0)
cout<<tree->key<<" is root"<<endl;
else
cout<<tree->key<<" is "<<key<<"'s"<<(direction==1?"right child":"left child")<<endl;
print(tree->l,tree->key,-1);
print(tree->r,tree->key,1);
}
}
//列印(外部介面)
template<class T>
void BSTree<T>::print()
{
if(root) print(root,root->key,0);
}
//銷燬(內部介面)
template<class T>
void BSTree<T>::destroy(Node<T>* &tree)
{
if(tree)
{
if(tree->l) destroy(tree->l);
if(tree->r) destroy(tree->r);
delete tree;
tree=NULL;
}
}
//外部介面
template<class T>
void BSTree<T>::destroy()
{
destroy(root);
}