1. 程式人生 > >資料結構——二叉排序樹

資料結構——二叉排序樹

建立、查詢、刪除

codeblocks 17 通過

#include <iostream>
#define KeyType int
#define InfoType char
using namespace std;

typedef struct
{
    KeyType key;    // 關鍵字項
    InfoType otherinfo;  // 其他資料項
}ElemType;  // 每個節點的資料型別
typedef struct BSTNode
{
    ElemType data;  // 節點的資料項
    struct BSTNode *lchild, *rchild;
}BSTNode,*BSTree;

void InsertBST(BSTree &T, ElemType e)
{
    if(!T)  // T根節點為空
    {
        BSTree S = new BSTNode;
        S->data = e;
        S->lchild = S->rchild = NULL;
        T=S;
    }
    else if(e.key < T->data.key)
        InsertBST(T->lchild,e);
    else if(e.key > T->data.key)
        InsertBST(T->rchild,e);
}

void CreateBSTree(BSTree &T)
{
    T = NULL;   // 初始化為空樹
    int n=15;
    //cin>>n:
    while(n>0)
    {
        ElemType value;
        cin>>value.key;
        InsertBST(T,value);
        n--;
    }
    //cout<<"Create Done !"<<endl;
}

BSTree SearchBST(BSTree &T, KeyType key)
{
    if((!T)||key==T->data.key)
        return T;   // 查詢結束 返回目標節點或者空指標
    else if(key<T->data.key)
        return SearchBST(T->lchild,key);    // 在左子樹中繼續查詢
    else
        return SearchBST(T->rchild,key);    // 在右子樹中繼續查詢
}

void InOrderTraverse(BSTree T)
{// 中序遞迴遍歷
    if(T)
    {
        InOrderTraverse(T->lchild);
        cout<<T->data.key<<" ";
        InOrderTraverse(T->rchild);
    }
}

void DeleteBST(BSTree &T, KeyType key)
{// 從二叉排序樹T中刪除關鍵字等於key的節點
    BSTree p = T, f=NULL, q;    // 初始化
    /*------下面的while迴圈從根開始查詢關鍵字等於key的節點--------*/
    while(p)
    {
        if(p->data.key == key)
            break;
        f = p;
        if(p->data.key > key)
            p = p->lchild;
        else
            p = p->rchild;
    }
    /*--------分別考慮三種情況,p所指的子樹內部:左右子樹都不為空,無左子樹,無右子樹---------*/
    q = p;
    if((p->lchild)&&(p->rchild))    // 左右子樹都不為空
    {
        BSTree s = p->lchild;
        while(s->rchild)    // 查詢p的左子樹的最右節點,即p的直接前驅
        {
            q=s;s=s->rchild;
        }
        p->data=s->data;    // 將s的data 賦值給要刪除的節點p,p不能直接delete,需要保持p的左右子樹關係
        if(q!=p)    // 如果pq不相同,說明s有右子樹
            q->rchild=s->lchild;
        else    // qp相同,說明s沒有右子樹
            q->lchild=s->lchild;
        delete s;
        return; // 刪除結束!!!
    }
    else if(!p->rchild)   // 無右子樹,只需要重接左子樹
    {
        p = p->lchild;
    }
    else if(!p->lchild)   // 無左子樹,只需要重接右子樹
    {
        p = p->rchild;
    }
    /*-------將p所指的子樹掛接到其雙親節點*f相應位置-----------*/
    if(!f)  T=p;    // 如果刪除的是根節點
    else if(q==f->lchild)   //  q是f的左子樹,p接到f的左子樹位置
        f->lchild=p;
    else    // q是f的右子樹,p接到f的右子樹位置
        f->rchild=p;
    delete q;
}

int main()
{
    BSTree T;
    CreateBSTree(T);
    InOrderTraverse(T);
    cout<<"\n";
    BSTree p;
    KeyType key;
    cin>>key;
    p = SearchBST(T,key);
    if(p)
        cout<<"查詢成功!"<<endl;
    else
        cout<<"not find it!"<<endl;
    cin>>key;
    DeleteBST(T,key);
    InOrderTraverse(T);

}


/*
test data:
輸入:
1 5 4 2 3 6 8 7 9 11 14 13 12 16 19
輸出:
1 2 3 4 5 6 7 8 9 11 12 13 14 16 19
輸入:
19
輸出:
查詢成功!
輸入:
14
輸出:
1 2 3 4 5 6 7 8 9 11 12 13 19 16

*/

附圖便於思考刪除演算法

 

 

小甲魚老師的程式碼,我加了註釋:

int Delete(BSTree &p)
{
    BSTree q, s;
    /*-----待刪除的節點只有左或右子樹----*/
    if(p->rchild == NULL)
    {
        q = p;
        p = p->lchild;
        delete q;
    }
    else if(p->lchild == NULL)
    {
        q = p;
        p = p->rchild;
        delete q;
    }
    /*-----待刪除的節點有左和右子樹-------*/
    else
    {
        q = p;
        s = p->lchild;

        while(s->rchild)
        {// 查詢p的直接前驅,s記錄,q是s的雙親
            q = s;
            s = s->rchild;
        }

        p->data.key = s->data.key;  // 資料替換

        if(q != p)  // qp不想同,說明p的左子樹有右子樹
            q->rchild = s->lchild;
       else // qp相同,說明p的左子樹沒有右子樹
            q->lchild = s->lchild;

       delete s;
    }

    return 1;
}
int DeleteBST_JiaYu(BSTree &T, KeyType key)
{// 返回刪除結果,該函式也可通過呼叫search函式進行查詢,再刪除。
    if(!T)
        return -1;
    else
    {
        if(key == T->data.key)
            return Delete(T);
        else if(key>T->data.key)
            return DeleteBST_JiaYu(T->rchild,key);
        else
            return DeleteBST_JiaYu(T->lchild,key);
    }
}

 另附圖:

 

 

完整程式碼code

 

// 二叉排序樹 @ChenYe 2018/12/02
#include <iostream>
#define KeyType int
#define InfoType char
using namespace std;

typedef struct
{
    KeyType key;    // 關鍵字項
    InfoType otherinfo;  // 其他資料項
}ElemType;  // 每個節點的資料型別
typedef struct BSTNode
{
    ElemType data;  // 節點的資料項
    struct BSTNode *lchild, *rchild;
}BSTNode,*BSTree;

void InsertBST(BSTree &T, ElemType e)
{
    if(!T)  // T根節點為空
    {
        BSTree S = new BSTNode;
        S->data = e;
        S->lchild = S->rchild = NULL;
        T=S;
    }
    else if(e.key < T->data.key)
        InsertBST(T->lchild,e);
    else if(e.key > T->data.key)
        InsertBST(T->rchild,e);
}

void CreateBSTree(BSTree &T)
{
    T = NULL;   // 初始化為空樹
    int n=15;
    //cin>>n:
    while(n>0)
    {
        ElemType value;
        cin>>value.key;
        InsertBST(T,value);
        n--;
    }
    //cout<<"Create Done !"<<endl;
}

BSTree SearchBST(BSTree &T, KeyType key)
{
    if((!T)||key==T->data.key)
        return T;   // 查詢結束 返回目標節點或者空指標
    else if(key<T->data.key)
        return SearchBST(T->lchild,key);    // 在左子樹中繼續查詢
    else
        return SearchBST(T->rchild,key);    // 在右子樹中繼續查詢
}

void InOrderTraverse(BSTree T)
{// 中序遞迴遍歷
    if(T)
    {
        InOrderTraverse(T->lchild);
        cout<<T->data.key<<" ";
        InOrderTraverse(T->rchild);
    }
}

void DeleteBST(BSTree &T, KeyType key)
{// 從二叉排序樹T中刪除關鍵字等於key的節點
    BSTree p = T, f=NULL, q;    // 初始化
    /*------下面的while迴圈從根開始查詢關鍵字等於key的節點--------*/
    while(p)
    {
        if(p->data.key == key)
            break;
        f = p;
        if(p->data.key > key)
            p = p->lchild;
        else
            p = p->rchild;
    }
    /*--------分別考慮三種情況,p所指的子樹內部:左右子樹都不為空,無左子樹,無右子樹---------*/
    q = p;
    if((p->lchild)&&(p->rchild))    // 左右子樹都不為空
    {
        BSTree s = p->lchild;
        while(s->rchild)    // 查詢p的左子樹的最右節點,即p的直接前驅
        {
            q=s;s=s->rchild;
        }
        p->data=s->data;    // 將s的data 賦值給要刪除的節點p,p不能直接delete,需要保持p的左右子樹關係
        if(q!=p)    // 如果pq不相同,說明s有右子樹
            q->rchild=s->lchild;
        else    // qp相同,說明s沒有右子樹
            q->lchild=s->lchild;
        delete s;
        return; // 刪除結束!!!
    }
    else if(!p->rchild)   // 無右子樹,只需要重接左子樹
    {
        p = p->lchild;
    }
    else if(!p->lchild)   // 無左子樹,只需要重接右子樹
    {
        p = p->rchild;
    }
    /*-------將p所指的子樹掛接到其雙親節點*f相應位置-----------*/
    if(!f)  T=p;    // 如果刪除的是根節點
    else if(q==f->lchild)   //  q是f的左子樹,p接到f的左子樹位置
        f->lchild=p;
    else    // q是f的右子樹,p接到f的右子樹位置
        f->rchild=p;
    delete q;
}

int Delete(BSTree &p)
{
    BSTree q, s;
    /*-----待刪除的節點只有左或右子樹----*/
    if(p->rchild == NULL)
    {
        q = p;
        p = p->lchild;
        delete q;
    }
    else if(p->lchild == NULL)
    {
        q = p;
        p = p->rchild;
        delete q;
    }
    /*-----待刪除的節點有左和右子樹-------*/
    else
    {
        q = p;
        s = p->lchild;

        while(s->rchild)
        {// 查詢p的直接前驅,s記錄,q是s的雙親
            q = s;
            s = s->rchild;
        }

        p->data.key = s->data.key;  // 資料替換

        if(q != p)  // qp不想同,說明p的左子樹有右子樹
            q->rchild = s->lchild;
       else // qp相同,說明p的左子樹沒有右子樹
            q->lchild = s->lchild;

       delete s;
    }

    return 1;
}
int DeleteBST_JiaYu(BSTree &T, KeyType key)
{// 返回刪除結果,該函式也可通過呼叫search函式進行查詢,再刪除。
    if(!T)
        return -1;
    else
    {
        if(key == T->data.key)
            return Delete(T);
        else if(key>T->data.key)
            return DeleteBST_JiaYu(T->rchild,key);
        else
            return DeleteBST_JiaYu(T->lchild,key);
    }
}


int main()
{
    BSTree T;
    CreateBSTree(T);
    InOrderTraverse(T);
    cout<<"\n";
    BSTree p;
    KeyType key;
    cin>>key;
    p = SearchBST(T,key);
    if(p)
        cout<<"查詢成功!"<<endl;
    else
        cout<<"not find it!"<<endl;
    cin>>key;
    DeleteBST(T,key);
    InOrderTraverse(T);


    cout<<"\n新增測試,請輸入要刪除的資料:\n";
    cin>>key;
    DeleteBST_JiaYu(T,key);
    InOrderTraverse(T);

}


/*
test data:
輸入:
1 5 4 2 3 6 8 7 9 11 14 13 12 16 19
輸出:
1 2 3 4 5 6 7 8 9 11 12 13 14 16 19
輸入:
19
輸出:
查詢成功!
輸入:
14
輸出:
1 2 3 4 5 6 7 8 9 11 12 13 19 16

*/

 測試