算法學習--二叉搜索樹

二叉搜索樹支持動態數據操作,二叉搜索樹結構可用於字典,也可以用來實現優先隊列。二叉搜索樹結構建立在二叉樹上,可以用鏈表結構來實現。每個結點包含的信息有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()函數返還申請的內存。









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