1. 程式人生 > >資料結構與演算法(C語言) | 二叉排序樹

資料結構與演算法(C語言) | 二叉排序樹

二叉排序樹的定義——

二叉排序樹 ( Binary Sort Tree) 或者為空;或者是具有如下特性的二叉樹:

(1)若根的左子樹不空,則左子樹上所有結點的關鍵字均小於根結點的關鍵字;

(2)若根的右子樹不空,則右子樹上所有結點的關鍵字均大於根結點的關鍵字;

(3)根的左、右子樹也分別是二叉排序樹。

     一棵二叉排序樹的中序遍歷結果:關鍵字按從小到大的順序排列。

二叉排序樹的查詢——

若二叉排序樹為空,則查詢不成功;否則

1)若給定值等於根結點的關鍵字,則查詢 成功;

2)若給定值小於根結點的關鍵字,則繼續在左子樹上進行查詢;

3)若給定值大於根結點的關鍵字,則繼續在右子樹上進行查詢。

// 二叉樹的二叉連結串列結點結構定義
typedef struct BiTNode
{
    int data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

// 遞迴查詢二叉排序樹 T 中是否存在 key
// 指標 f 指向 T 的雙親,其初始值呼叫值為 NULL
// 若查詢成功,則指標 p 指向該資料元素結點,並返回 TRUE
// 否則指標 p 指向查詢路徑上訪問的最後一個結點,並返回 FALSE
Status SearchBST(BiTree T, int key, BiTree f, BiTree *p)
{
    if( !T )    // 查詢不成功
    {
        *p = f;
        return FALSE;
    }
    else if( key == T->data )   // 查詢成功
    {
        *p = T;
        return TRUE;
    }
    else if( key < T->data )
    {
        return SearchBST( T->lchild, key, T, p );   // 在左子樹繼續查詢
    }
    else
    {
        return SearchBST( T->rchild, key, T, p );   // 在右子樹繼續查詢
    }
}

二叉排序樹的插入——

    若二叉排序樹為空,則新結點做根,否則從根出發進行查詢。

    查詢成功時空操作,查詢不成功時將新結點作為查詢路徑上最後一個結點的左孩子或右孩子。

    新結點總是作為二叉排序樹的葉子。

    二叉排序樹的插入操作之中包含著一個查詢操作,該查詢操作應具備兩項功能:

1)確定要插入的關鍵字 key 在二叉排序樹中是否存在;

2)當查詢不成功時返回插入位置(即查詢路徑上最後一個結點的地址)。

// 當二叉排序樹 T 中不存在關鍵字等於 key 的資料元素時,
// 插入 key 並返回 TRUE,否則返回 FALSE
Status InsertBST(BiTree *T, int key)
{
    BiTree p, s;
    if( !SearchBST(*T, key, NULL, &p) )
    {
        s = (BiTree)malloc(sizeof(BiTNode));
        s->data = key;
        s->lchild = s->rchild = NULL;
        
        if( !p )       
        {
            *T = s;     // 插入 s 為新的根結點
        }
        else if( key < p->data )
        {
            p->lchild = s;  // 插入 s 為左孩子
        }
        else
        {
            p->rchild = s;  // 插入 s 為右孩子
        }
        return TRUE;
    }
    else
    {
        return FALSE;   // 樹中已有關鍵字相同的結點,不再插入
    }
}

二叉排序樹的刪除——

二叉排序樹中結點的刪除操作只能在查詢成功的前提下進行,並且在二叉排序樹上刪除某個結點之後,應使其保持二叉排序樹的特性。

刪除方法可分三種情況討論:

(1)被刪除的結點是葉子;——其雙親結點中相應指標域的值改為“空”

(2)被刪除的結點只有左子樹只有右子樹;——    其雙親結點的相應指標域的值改為指向被刪除結點的左子樹或右子樹的根。

(3)被刪除的結點既有左子樹,也有右子樹。——以其前驅替代之,然後再刪除該前驅結點

Status DeleteBST(BiTree *T, int key)
{
    if( !*T )
    {
        return FALSE;
    }
    else
    {
        if( key == (*T)->data )
        {
            return Delete(T);
        }
        else if( key < (*T)->data )
        {
            return DeleteBST(&(*T)->lchild, key);
        }
        else
        {
            return DeleteBST(&(*T)->rchild, key);
        }
    }
}

Status Delete(BiTree *p)
{
	//q用來儲存雙親結點 
    BiTree q, s;
    
    if( (*p)->rchild == NULL )
    {
        q = *p;
        *p = (*p)->lchild;
        free(q);
    }
    else if( (*p)->lchild == NULL )
    {
        q = *p;
        *p = (*p)->rchild;
        free(q);
    }
    else
    {
        q = *p;
        s = (*p)->lchild;
        
        //迭代找出直接前驅
		//最右子樹 
        while( s->rchild )
        {
            q = s;
            s = s->rchild;
        }
        
        //替換資料 
        (*p)->data = s->data;
        
        if( q != *p )
        {
            q->rchild = s->lchild;
        }
        else
        {
            q->lchild = s->lchild;
        }
        
        free(s);
    }
    
    return TRUE;
}

二叉排序樹查詢效能的分析——

    由於新插入的結點總是做葉子,所以二叉排序樹的形態與關鍵字的插入順序有關。

    由值相同的 n 個關鍵字,構造所得的不同形態的各棵二叉排序樹的平均查詢長度的值不同,甚至可能差別很大。