二分查找树BST(也叫二叉查找树、二叉排序树)的提出是为了提供查找效率,之所以称为二分查找树,因为该二叉树对应着二分查找算法,查找平均的时间复杂度为o(logn),所以该数据结构的提出是为了提高查找效率。
定义
二分查找树或者是一棵空树,或者具有下列性质:
1.若它的左子树不为空,则左子树上所有结点的值均小于根结点的值;
2.若它的右子树不为空,则右子树上所有结点的值均大于根结点的值;
3.它的左右子树均为二分查找树。
操作
二分查找树的操作主要包括:插入、查找、删除。
1.插入操作
设s指向待插入的结点,root指向二叉查找树的根结点,则插入操作的步骤如下:
(1)若root为空,则将s指向的结点作为跟结点插入,否则执行(2)、(3);
(2)若s->data < root->data,则将s指向的结点插入到根结点的左子树中;
(3)若s->data > root->data,则将s指向的结点插入到根结点的右子树中。
总结:二叉树的构造就是通过不断地插入新的元素。
2.查找操作
在二分查找树中查找给定值k的查找过程如下:
(1)若root=NULL,则查找失败;
(2)若root->data=k,则查找成功;
(3)若k < root->data,则去root的左边查找;
(4)若k > root->data,则去root的右边查找。
总结:若二分查找树接近平衡二叉树,则其时间复杂度为O(logn),若二分查找树是斜的(如插入是有序插入的情况下),则其实际复杂度为O(n),即退化为线性表。
3.删除操作
设p指向待删除的结点,pre指向待删除结点的父亲,则删除操作视如下情况而定:
(1)若待删除的结点是叶子结点,不妨设pre->right=p(即待删除的结点为其父亲结点的右孩子),则直接删除p,对应为:pre->right=NULL,delete p;
(2)若待删除的结点只有左子树或右子树,则只需将待删除结点的父亲与左孩子(或右孩子)连接起来,对应为,不妨设pre->right=p,以待删除结点仅有左子树的情况为例(右子树同理),对应为:pre->right=p->left,delete p;
(3)若待删除结点左右子树都有,则执行如下步骤:
总体来说,整个线索是:找到右子树的最小值结点-->连接断开结点-->对最小值结点的上下文做善后工作。
I.先找到待删除结点的右子树中的最小值(或左子树中的最大值),对应的指针为min,并记下min的父亲结点为min_pre;
II.用min所指结值覆盖待删结点的值,对应为:p->data=min->data;
III.分两种情况(如下图所示):
![](https://img-blog.csdn.net/20140702194920093?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
特殊情况:若待删除结点的右孩子无左子树,也就是说待删结点的右孩子就是右子树的最大值,则直接连接即可,对应为:p->right=min->right,delete min;
一般情况:若待删除结点的右孩子有左子树,则将min_pre所指结点的右孩子指向min所只结点的右孩子,对应为:min_pre->right=min->right,delete min;
举例:
假设有下图所示的二分查找树,现在我们分别对该树做杉树情况的删除操作。
![](https://img-blog.csdn.net/20140702202937343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
删除元素1(叶子结点):
![](https://img-blog.csdn.net/20140702203705609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
删除元素9(只有一个孩子):
![](https://img-blog.csdn.net/20140702203750093?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
删除元素7(有左右孩子)
![](https://img-blog.csdn.net/20140702203829015?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
4.懒惰删除
通常情况下的删除策略都是用待删除结点的右子树中的最小结点来替换待删除的结点,但这种删除方法的效率其实不高,因为它沿着该树进行两趟搜索,第一次搜索是为了找到待删除的结点的位置,即p,第二趟搜索是为了找到最小的替补,即min。如果删除的次数不多,则通常采用的策略是懒惰删除(lazy deletion)。
懒惰删除的思想是:当一个元素被删除时,它仍然留在树中,只对它做删除标记。下次我们做搜素操作时,把该元素当作是一个存在的元素(当然,如果查找的元素就是该元素,我们只要看下该元素是否有删除标记),当作删除操作时,该元素被当作一个空元素,当在带有删除标记的点处插入新元素时,就把该位置当作一个空位置。
5.含有重复元素的二分查找树
含有重复元素的二分查找树的难题在于如何元素插入的问题,这里提供了一种解决的方法:通过在每个结点记录中保留一个附加域以指示该结点元素出现的次数。当然这也大大增加了附加空间,但是却比重复信息放到树中要好(因为如果将重复元素放到树中会增加树的深度)。
元素的删除问题:由于树中可能存在重复的元素,这时候我们可以采用懒惰删除。即当元素出现删除操作时,就将该元素出现的次数减1,若减到次数为0,则将该元素标记为被删除状态(而实际上并未真正被删除)。
测试代码如下:
- #include<iostream>
- using namespace std;
- struct Node
- {
- int data;
- Node *left;
- Node *right;
- Node(int data)
- {
- this->data=data;
- this->left=NULL;
- this->right=NULL;
- }
- };
- class BST
- {
- public:
- Node *root;
-
- void Insert(int value);
- void Insert(Node* &r,Node *p);
- Node* Insert1(Node *r,Node *p);
-
- void BuildBST(int a[],int n);
-
- BST():root(NULL){}
- Node* Search(Node *r,int value);
- Node *FindMax(Node *r);
- Node *FindMin(Node *r);
- void Delete(Node* &r,int value);
-
-
- void PreOrder(Node *r);
- void InOrder(Node *r);
- void PostOrder(Node *r);
-
- };
-
- void BST::Insert(int value)
- {
- Node *p=root;
- Node *pre=p;
- while(p)
- {
- if(value > p->data)
- {
- pre=p;
- p=p->right;
- }
- else if(value < p->data)
- {
- pre=p;
- p=p->left;
- }
- else
- {
- cout<<"树中已经存在值"<<value<<",插入失败!"<<endl;
- return ;
- }
- }
- if(value > pre->data)
- {
- pre->right=new Node(value);
- }
- else
- {
- pre->left=new Node(value);
- }
- }
- void BST::Insert(Node * &r,Node *p)
- {
- if(r==NULL)
- {
- r=p;
- }
- else if(p->data > r->data)
- {
- Insert(r->right,p);
- }
- else
- {
- Insert(r->left,p);
- }
- }
-
- Node* BST::Insert1(Node *r,Node *p)
- {
- if(r==NULL)
- {
- r=p;
- }
- else if(p->data > r->data)
- {
- Insert(r->right,p);
- }
- else
- {
- Insert(r->left,p);
- }
- return r;
- }
-
- void BST::BuildBST(int a[],int n)
- {
- int i;
- for(i=0;i<n;i++)
- {
- Node *s=new Node(a[i]);
- root=Insert1(root,s);
- }
- }
-
- void BST::PreOrder(Node *r)
- {
- if(r==NULL)
- {
- return ;
- }
- else
- {
- cout<<r->data<<" ";
- this->PreOrder(r->left);
- this->PreOrder(r->right);
- }
- }
- void BST::InOrder(Node *r)
- {
- if(r==NULL)
- {
- return ;
- }
- else
- {
- InOrder(r->left);
- cout<<r->data<<" ";
- InOrder(r->right);
- }
- }
-
- void BST::PostOrder(Node *r)
- {
- if(r==NULL)
- {
- return ;
- }
- else
- {
- PostOrder(r->left);
- PostOrder(r->right);
- cout<<r->data<<" ";
- }
- }
-
- Node* BST::Search(Node *r,int value)
- {
- if(r==NULL)
- {
- return NULL;
- }
- if(r->data==value)
- {
- return r;
- }
- else if (value > r->data)
- {
- return Search(r->right,value);
- }
- else
- {
- return Search(r->left,value);
- }
- }
- Node* BST::FindMax(Node *r)
- {
- if(r==NULL)
- {
- return NULL;
- }
- else if(r->right==NULL)
- {
- return r;
- }
- else
- {
- return FindMax(r->right);
- }
- }
- Node* BST::FindMin(Node *r)
- {
- if(r==NULL)
- {
- return NULL;
- }
- else if(r->left==NULL)
- {
- return r;
- }
- else
- {
- return FindMin(r->left);
- }
- }
- void BST::Delete(Node* &r,int value)
- {
-
- if(r==NULL)
- {
- return ;
- }
- Node *pre=NULL,*p=NULL,*min_pre=NULL,*min=NULL;
- pre=p=r;
-
- while(p)
- {
- if(p->data==value)
- {
- break;
- }
- else if(value > p->data)
- {
- pre=p;
- p=p->right;
- }
- else
- {
- pre=p;
- p=p->left;
- }
- }
-
- if(p)
- {
- bool hasTwo=true;
- min_pre=min=p->right;
- if(pre->left==p)
- {
- if(!p->left && !p->right)
- {
- pre->left=NULL;
- delete p;
- hasTwo=false;
- }
- else if(!p->right)
- {
- pre->left=p->left;
- delete p;
- hasTwo=false;
- }
- else if(!p->left)
- {
- pre->left=p->right;
- delete p;
- hasTwo=false;
- }
- }
- else
- {
- if(!p->left && !p->right)
- {
- pre->right=NULL;
- delete p;
- hasTwo=false;
- }
- else if(!p->right)
- {
- pre->right=p->left;
- delete p;
- hasTwo=false;
- }
- else if(!p->left)
- {
- pre->right=p->right;
- delete p;
- hasTwo=false;
- }
- }
- if(hasTwo)
- {
- min_pre=p;
- min=p->right;
- while(min->left)
- {
- min_pre=min;
- min=min->left;
- }
- p->data=min->data;
- if(min_pre==p)
- {
- p->right=min->right;
- }
- else
- {
- min_pre->left=min->right;
- }
- delete min;
-
- }
- }
- }
-
- int main()
- {
- int a[]={3,1,7,5,11,9,10,12};
- BST bst;
- bst.BuildBST(a,sizeof(a)/sizeof(int));
-
- cout<<"先序遍历结果: ";
- bst.PreOrder(bst.root);
- cout<<endl;
- cout<<"中序遍历结果: ";
- bst.InOrder(bst.root);
- cout<<endl;
- cout<<"后序遍历结果: ";
- bst.PostOrder(bst.root);
- cout<<endl<<endl;
-
- cout<<"插入值6,后删除中序遍历结果:"<<endl;
- bst.Insert(6);
- bst.InOrder(bst.root);
- cout<<endl<<endl;
-
- cout<<"插入值10";
- bst.Insert(10);
- cout<<endl;
-
- cout<<"删除7后删除中序遍历结果:"<<endl;
- bst.Delete(bst.root,7);
- bst.InOrder(bst.root);
- cout<<endl;
- return 0;
- }
测试结果如下:
![](https://img-blog.csdn.net/20140702204958703?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSlhIXzEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)