算法学习--二叉搜索树

二叉搜索树支持动态数据操作,二叉搜索树结构可用于字典,也可以用来实现优先队列。二叉搜索树结构建立在二叉树上,可以用链表结构来实现。每个结点包含的信息有left该结点的左子结点,right该结点的右子结点,parent该结点的双亲结点,key查询关键字。如果有需要可以包含data,某种具体的数据。因为使用到链式结构,所以需要使用到malloc,或者是new函数。在后面的代码中博主使用malloc函数来动态申请内存。

二叉搜索树的操作都需要建立在二叉搜索树上,所以我打算先谈一下树的建立。与之前讲到的堆不同,二叉搜索树的建立需要的是不断插入。

插入部分代码:

void Tree_Insert(Tree *root,Tree *z)
{
    Tree *x,*y;
    y=NULL;
    x=root;
    while (x!=NULL)
    {
        y=x;
        if (x->key>z->key)
        {
            x=x->left;
        }
        else if (x->key<z->key)
        {
            x=x->right;
        }
        else
        {
            return ;     //说明树中已经存在一样的结点,不用再插入
        }
    }
    z->parent=y;
    if (y==NULL)
    {
        root=z;
    }
    else if (z->key<y->key)
    {
        y->left=z;
    }
    else
    {
        y->right=z;
    }
}
二叉搜索树的遍历有三种:先序遍历,中序遍历,后续遍历。根据当前结点输出,考察先后定义。先序:在其左右子结点之前输出,考察;中序:在左结点,右结点之间输出,考察;后序:在其左右子结点考察,输出后再考察。

先序:

void Print_Tree(Tree *x)
{
    if (x!=NULL)
    {
        cout<<x->key<<endl;
        Print_Tree(x->left);
        Print_Tree(x->right);
    }
}
中序:

void Print_Tree(Tree *x)

{
    if (x!=NULL)
    {
        Print_Tree(x->left);
        cout<<x->key<<endl;
        Print_Tree(x->right);
    }
}
后序:

void Print_Tree(Tree *x)

{
    if (x!=NULL)
    {
        Print_Tree(x->left);
        Print_Tree(x->right);
        cout<<x->key<<endl;
    }
}
查找依照key值进行查找,找到对应之后返回,若没有对应值将会返回NULL

Tree* Search(Tree *x,key)

{
    while (x!=NULL||key==x->key)
    {
        if (x->key>key)
            x=x->left;
        else if (x->key<key)
            x=x->right;
    }
    return x;
}

MAX()函数用于返回key值最大的结点,MIN()返回key值最小的结点。因为这两个函数以及二叉搜索树的特征使得能够使用二叉搜索树来实现优先队列,但似乎效果没有堆的好。

堆:http://blog.csdn.net/hermit_inwind/article/details/50413255

Tree* MIN(Tree *x)
{
    while (x->left!=NULL)
        x=x->left;
    return x;
}

Tree* MAX(Tree *x)
{
    while (x->right!=NULL)
        x=x.right;
    return x;
}


删除二叉搜索树中的结点时,会出现三种情:该结点没有子结点;该结点没有左结点,右结点中的一个;该结点有左右结点。在解决删除结点的过程中需要用到Transplant()函数来解决,该函数的作用是用一个子树替换另一个子树。

Transplant(Tree *root,Tree *u,Tree *v)
{
	if (u->parent==NULL)
		root=v;
	else if (u->parent->left==u)
		u->parent->left=v;
	else
		u->parent->right=v;
	if (v!=NULL)
		v->parent=u->parent;
}
显然,当u没有前驱结点的时候,u就为root结点,将root替换为v即可。除开这种情况,先判断u为其双亲结点的左结点还是右结点,然后将v指向的双亲结点替换为u指向的双亲结点即可。

接下来在实现删除的时候使用到Transplant()函数会然代码看上去更精简,更美观。

void Delet(Tree *root,Tree *x)
{
    if (x->left==NULL)
        Transplant(root,x,x->right);
    else if (x->right==NULL)
        Transplant(root,x,x-left);
    else
    {
        Tree *y=MIN(x->right);
        if (y->parent!=x)
        {
            Transplant(root,y,y->right);
            y->right=x->right;
            y->right->parent=y;
        }
        Transplant(root,x,y);
        y->left=x->left;
        y->left->parent=y;
    }
    free(x);		//需要返还动态申请的内存
}



Delete实现:先判断需要删除的子树的根结点是否存在子结点中的一个,若不存在左结点,那么只需要将当前结点的右结点为根的子树替换当前子树即可。简单来说就是将删除结点的右节点的双亲结点更新为删除结点的双亲结点,删除结点的双亲结点的右结点更新为删除结点的右结点。若删除结点的右孩子不存在,则将删除结点与其左孩子替换。若删除结点的左右子树均存在,那么先求出删除结点右子树中的最小值,显然该结点的key值大于所有删除结点左子树中的结点,小于除开该结点以外所有删除结点右子树中的结点。当求出的key值最小结点就是删除结点的右结点时,只需要将该结点与删除结点替换即可,否则需要将该结点与其右结点替换(此时之前求出的结点的指向的右结点为删除结点的右子结点),然后将求出结点与删除结点替换,注意需要调整删除结点左子结点与替换结点之间的指向关系。最后,由于之前结点的内存是动态申请的,在删除后注意使用free()函数返还申请的内存。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章