数据结构基础(5)------------二叉排序树(BST)

来源:互联网 发布:巨杉数据库java笔试 编辑:程序博客网 时间:2024/06/05 22:33

                                                             数据结构基础(5)------------二叉排序树(BST)

                                                                   (一个递归的经典应用)

1.概念:二叉排序树,又名为二叉查找树。要么是一棵空树,要么是具有以下三性质的二叉树:

        A.若其左子树不为空,则其左子树的所有结点的值均小于根结点的值;

        B.若其右子树不为空,则其右子树的所有结点的值均大于根结点的值;

        C.它的左右子树也分别为二叉树。        

 其定义依然采用了递归的定义方法!而且其元素之间满足一定的次序关系,使得在其上的操作复杂度为lgn(插入、删除、查找);其中在查找方面,其时间效率要比折半查找、插值查找好得多;

其中二叉树的中序遍历算法(inorder tree walk)允许我们顺序输出二叉树中的所有元素!

2.二叉链表的结点结构定义:

pLeftData pRighttypedef int ElemType;
typedef struct BiTNode
{
ElemType Data;
BiTNode *pLeft;
BiTNode *pRight;
} BiTNode,*BiTree;

3.中序遍历输出二叉排序树中的所有元素:

void InorderTreeWalker(BiTree p)
{
if (nullptr!=p)
{
InorderTreeWalker(p->pLeft);
cout<<p->Data<<endl;
InorderTreeWalker(p->pRight);
}
}

4.二叉排序树插入算法:

注意传递进去的应该是一个关于指针的引用。否则演示会失败,没有结果输出。因为如果传递给二叉排序树的是一个指向二叉树结点指针的话,我们会在其中改变此指针的值,由于传递进来的也是本指针的值,所有与函数传值是一样的,不会改变函数之外的值。所以这里可有两种方法进行解决:其一:利用C++的引用传递参数的方式,其二可以使用指向指针的指针来完成!

可能在网络中别处的代码不一样,但是我的这段代码在VS2012中编译运行无误!并通过测试!切记切记!

void BiTreeInsert(BiTree &T,ElemType e)
{
if (nullptr==T)
{
T=new BiTNode;
T->pLeft=T->pRight=nullptr;
T->Data=e;
return ;
}
if (T->Data<e)
{
BiTreeInsert(T->pRight,e);
}
else
{
BiTreeInsert(T->pLeft,e);
}
}


5.二叉树删除算法:

当然,课本里都有讲述,删除是比较麻烦的,因为牵涉到所删除的结点有无左右子树;可以分为三个情况来对此进行描述:
要删除的结点是叶结点,
既没有左子树,也没有又子树。
这种情况很简单,直接将其父结点的左孩子指针或右孩子指针设
空即可(要记得释放要删除结点的资源)
要删除的结点只有左子树
,或者只有右子树
则直接将子树整体移动到删除结点的位置即可既有左子树,
又有右子树的情况。
因为删除后仍要保持中序遍历是有有序的序列,故而可以用中
序遍历后此删除节点的前驱和后继来替换。这就是基本思想。

注意:在《大话数据结构》P324页面所给的删除代码是错误的,
因为其没有考虑到所要删除的结点是子结点的情况!然而要加上子结点的情况
就必须对书中所提的算法,就行修改,因为若是叶节点删除的话,就必须对双
亲结点的相应子树的指针进行赋值nullptr,但是这就得知道指向双亲结点的
指针,然而此时,所设计的结点数据结构里并没有指向双亲结点的指针,所
以有两种方法来解决这类问题:1.改变数据结构,使之包含有指向双亲结点
的指针,在《算法导论》中直接给出适应此的数据结构,但是相应的存储开
销也加大了。2.修改已有的算法。结合本文所讲的数据结构,我采用的
是第二种方法:
//一个递归函数的调用,目的是找到所要删除的结点及其双亲结点
void BiTreeDelete(BiTree T,ElemType e,BiTree father)
{
if (nullptr==T)
{
cout<<"Not Exists!"<<endl;
}
else
{
if (T->Data==e)
{
deleteNode(T,father);
}
else if (T->Data>e)
{
father=T;
BiTreeDelete(T->pLeft,e,father);
}
else
{
father=T;
BiTreeDelete(T->pRight,e,father);
}
}
}


//将所要删除的结点,以及双亲结点,以引用的方式传递给下面的函数,

进行结点的删除以及双亲结点的设置工作!

void deleteNode(BiTree &T,BiTree &father)
{
BiTree p,s;
if (nullptr==T->pLeft && nullptr==T->pRight)
{
if (father->pRight==T)
{
father->pRight=nullptr;
}
else
{
father->pLeft=nullptr;
}
delete T;
return ;

}
else if (nullptr==T->pLeft)  //左子树为空,重接右子树
{
p=T;
T=T->pRight;
delete p;
}
else if (nullptr==T->pRight)
{
p=T;
T=T->pLeft;
delete p;
}
else  //左右子树均不为空
{
   p=T->pLeft;
s=T;
while (p->pRight)  //转左,向右到底找到待删节点的直接前驱
{
s=p;           //s指向待删节点的前驱的双亲结点
p=p->pRight;
}
T->Data=p->Data;
if (s==T)   //如果二者相等,则说明循环未执行,则重接左子树!
{
s->pLeft=p->pLeft;
}
else              //二者不相等。则将p的左子树,接到p双亲的右子树上,以继续保持中序遍历有序!
{
s->pRight=p->pLeft;
}
delete p;
}
}

卧浪居士

2014.3.11于HUST

参考文献《大话数据结构》


0 0
原创粉丝点击