您的位置:首页 > 其它

二叉排序树的建立、先序/中序/后序遍历、查找

2015-10-04 12:05 513 查看

一、定义与性质

定义 

  二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树(Binary Search Tree)。其定义为:二叉排序树或者是空树.

性质
  (1) 二叉排序树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字。
  (2) 二叉排序树中,各结点关键字是惟一的。
  注意:实际应用中,不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉排序树定义中BST性质(1)里的"小于"改为"大于等于",或将BST性质(2)里的"大于"改为"小于等于",甚至可同时修改这两个性质。
  (3) 按中序遍历该树所得到的中序序列是一个递增有序序列。

二、插入与删除

       插入与删除操作是二叉排序树中最常用也是最重要的两个操作。

       插入过程是:
  (a)若二叉排序树T为空,则为待插入的关键字key申请一个新结点,并令其为根;
  (b)若二叉排序树T不为空,则将key和根的关键字比较:
           (i)若二者相等,则说明树中已有此关键字key,无须插入。
           (ii)若key<T→key,则将key插入根的左子树中。
           (iii)若key>T→key,则将它插入根的右子树中。
  子树中的插入过程与上述的树中插入过程相同。如此进行下去,直到将key作为一个新的叶结点的关键字插入到二叉排序树中,或者直到发现树中已有此关键字为止。

     删除过程:
     

(1) 进行查找

       查找时,令p指向当前访问到的结点,parent指向其双亲(其初值为NULL)。若树中找不到被删结点则返回,否则被删结点是*p。

(2) 删去*p。

       删*p时,应将*p的子树(若有)仍连接在树上且保持BST性质不变。按*p的孩子数目分三种情况进行处理。

         删除*p结点的三种情况

         a.*p是叶子(即它的孩子数为0)

       无须连接*p的子树,只需将*p的双亲*parent中指向*p的指针域置空即可。

   

         b.*p只有一个孩子*child
       只需将*child和*p的双亲直接连接后,即可删去*p。
            注意:*p既可能是*parent的左孩子也可能是其右孩子,而*child可能是*p的左孩子或右孩子,故共有4种状态。

            c.*p有两个孩子

       先令q=p,将被删结点的地址保存在q中;然后找*q的中序后继*p,并在查找过程中仍用parent记住*p的双亲位置。*q的中序后继*p一定是*q的右子树中最左下的结点,它无左子树。因此,可以将删去*q的操作转换为删去的*p的操作,即在释放结点*p之前将其数据复制到*q中,就相当于删去了*q。


三、代码清单

#include<stdio.h>
#include<stdlib.h>
#define maxSize 20
#define maxWidth 20

typedef int KeyType; //假定关键字类型为整数
typedef struct node { //结点类型
KeyType key; //关键字项
struct node *lchild,*rchild;//左右孩子指针
} BSTNode,BSTree;
//typedef BSTNode *BSTree; //BSTree是二叉排序树的类型

//先序遍历
void preOrder(BSTree *BT)
{
if(BT!= NULL)
{
printf("%d-",BT->key);
preOrder(BT->lchild);
preOrder(BT->rchild);

}
}

//中序遍历
void inOrder(BSTree *BT)
{
if(BT!= NULL)
{
inOrder(BT->lchild);
printf("%d-",BT->key);
inOrder(BT->rchild);
}
}

//后序遍历
void postOrder(BSTree *BT)
{
if(BT!= NULL)
{
postOrder(BT->lchild);
postOrder(BT->rchild);
printf("%d-",BT->key);
}
}

//层次法打印二叉排序树
/* 以先序遍历的方式打印二叉排序树 */
void dispTree(BSTree *BT)
{
BSTree *stack[maxSize],*p;
int level[maxSize][2],top,n,i,width=4;
if(BT!=NULL)
{
printf("Display a tree by hollow means.\n");
top=1;
stack[top]=BT;//push root point to stack.
level[top][0]=width;

while(top>0)
{
p=stack[top];
n=level[top][0];
for(i=1;i<=n;i++)
printf(" ");
printf("%d",p->key);
for(i=n+1;i<maxWidth;i+=2)
printf("--");
printf("\n");
top--;

if(p->rchild!=NULL) //右子树先入栈,后出栈
{
top++;
stack[top]=p->rchild;
level[top][0]=n+width;
level[top][1]=2;
}

if(p->lchild!=NULL)  //左子树后入栈,先出栈
{
top++;
stack[top]=p->lchild;
level[top][0]=n+width;
level[top][1]=1;
}  //if
}  //while
}  //if
} //dispTree()

/* 向二叉排序树中加入一个结点
要改变指针,需要传递指针的指针*/
/* return 0表示插入成功, return -1表示插入失败 */
int InsertNode(BSTree **tree, KeyType key)
{
BSTNode *p= NULL, *parent = NULL;
BSTNode *pNewNode = (BSTNode *)malloc(sizeof(BSTNode));
if (pNewNode==NULL)
{
return -1;
}

/* 新建结点赋值,特别是左右子结点指针要赋值为NULL,叶子节点 */
/* 二叉排序树新插入的结点都是叶子节点 */
pNewNode->key = key;
pNewNode->lchild = NULL;
pNewNode->rchild = NULL;

/* 二叉排序树是空树 */
if (*tree==NULL)
{
*tree = pNewNode;
return 0;
}
else
{

p = *tree;
/* 寻找插入位置 */
while (NULL != p) /* 待插入的结点以叶子节点方式插入 */
{
/* key值已在二叉排序树中 */
if (p->key == key)
{
return 0;
}
else
{
parent = p;
p = (p->key < key) ? p->rchild : p->lchild; //key是待插入结点
}
} //while,结束时NULL == p,此时已经到了叶子节点位置
if (parent->key < key)
{
parent->rchild = pNewNode;
}
else
{
parent->lchild = pNewNode;
} //else
return 0;
} //else
} //InsertNode

//删除节点
/* 通过值查找并删除一个结点 */
int delNode(BSTree **tree, KeyType key)
{
BSTNode *p = NULL, *q = NULL, *parent = NULL, *child = NULL;
p = *tree;
/* parent为NULL表示根结点的父亲为NULL */
while (NULL != p)
{
if (p->key == key) //此时找到待删除的结点p
{
break;
}
else
{ parent = p;
p = (p->key < key) ? p->rchild : p->lchild;
}
} //while
/* p为NULL时, 表示没有找到结点值为key的结点 */
if (NULL == p) /* 到达叶子节点仍未查找到要删除的结点 */
{
return -1;
}
/* p, q现在都是保存了待删结点指针 */
q = p; //此时p->key == key

/* 待删结点有两个儿子结点,进行一下转化 */
if (NULL != p->lchild && NULL != p->rchild)
{
//找中序后继,先右拐,然后左走到底
parent = p;
p = p->rchild; /* 进入右子树 */
while (NULL != p->lchild)
{
parent = p;
p = p->lchild;
}
/* p中保存了待删结点右子树中最左下的结点指针, parent中就保存了该结点父亲指针 */
child = p->rchild;
}
else if(NULL == p -> lchild)
child = p -> rchild;
else
child = p -> lchild;

/* parent保存待删结点的父亲结点指针, child保存了待删结点的儿子结点

//实际删除的是待删节点的直接后继,下面是删除直接后继的过程,(待删结点至多只有一个儿子, 有两个会转化为0个或1个右结点)
*/
// 待删结点是根结点,且只有一个儿子
if (NULL == parent)
{
if(p->lchild!=NULL) *tree = p->lchild;
else *tree = p->rchild;
}
else
{
/*待删结点是父亲结点的左儿子*/
if (parent->lchild == p)
{
parent->lchild = child;
}
else
{
parent->rchild = child;
}
//将实际删除的节点的key值赋给原先要删除的节点
if (p != q)
{
q->key = p->key;
}
}
free(p);
return 0;
} //delNode

//二叉排序树查找
BSTNode* SearchBST(BSTree *T,KeyType key)
{ //在二叉排序树T上查找关键字为key的结点,成功时返回该结点位置,否则返回NUll
if(T==NULL) //递归的终结条件
return NULL; //T为空,查找失败;
if(key==T->key)
//成功,返回找到的结点位置
{
printf("Got it!");
return T;
}

if(key<T->key)
return SearchBST(T->lchild,key);
else
return SearchBST(T->rchild,key);//继续在右子树中查找
} //SearchBST

int main()
{
int n;
BSTree *B=NULL;
printf("Input number to initialize a BSTree:");
while(1)
{
scanf("%d",&n);
if(n==0) break; //遇到0时停止输入,0并不入树
InsertNode(&B, n);
}
dispTree(B);
printf("PreOrder:");
preOrder(B);
printf("\n");
printf("Search a node:");
scanf("%d",&n);
SearchBST(B,n);
printf("\n");
printf("Delete a node:");
scanf("%d",&n);
delNode(&B,n);
dispTree(B);
printf("PreOrder:");
preOrder(B);
printf("\n");
system("pause");
return 1;
}


四、程序运行结果



出处:http://blog.csdn.net/silangquan/article/details/8065243
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息