七、二叉樹(八)、二叉排序樹

數據結構目錄

一、定義

二叉排序樹

二叉排序樹(Binary Sort Tree)又稱為二叉查找樹,它或者是一顆空樹,或者是具有下列性質的二叉樹:

  • 若它的左子樹不為空,則左子樹上所有結點的值均小于它的根結構的值;
  • 若它的右子樹不為空,則右子樹上所有結點的值均大于它的根結構的值;
  • 它的左、右子樹也分別為二叉排序樹(遞歸)

中序遍歷二叉排序樹可以得到一個有序的列表

二、二叉排序樹的算法實現

定義和普通二叉樹的算法相同

typedef enum Status{
    success = 0,//成功
    fail = 1//失敗
}Status;

//二叉樹結點定義
typedef struct SearchBinaryTreeNode{
    int data;
    struct SearchBinaryTreeNode *lChild,*rChild;
}SearchBinaryTreeNode,*SearchBinaryTree;

1.查找算法

/// 查找二叉排序樹上的值為key的結點
/// @param T 二叉樹的根節點
/// @param key 搜索的值
/// @param f 指向最終查找出來結點的雙親,一開始為NULL,也就是指向二叉樹根節點的雙親,當然為NULL
/// @param p 查找成功則返回查找到的結點,并return success,否則指向查找路徑上最后訪問的結點,并返回fail
Status searchBst(SearchBinaryTree T,int key,SearchBinaryTree f,SearchBinaryTree *p){
    if (!T) {
        //查找不成功
        *p = f;
        return fail;
    } else if (key == T->data){
        //查找成功
        *p = T;
        return success;
    } else if (key < T->data){
        //在左子樹查找 遞歸
        searchBst(T->lChild, key, T, p);
    } else if (key > T->data){
        //在右子樹查找 遞歸
        searchBst(T->rChild, key, T, p);
    }
    
    
    return fail;
}

2.插入算法(也可以作為生成算法)

/// 插入操作  這個也可以作為二叉排序樹的生成算法
/// @param T 二叉樹的根節點
/// @param key 插入key并返回success,否則返回fail
Status insertBst(SearchBinaryTree *T,int key){
    SearchBinaryTree p,s;
    if (searchBst(*T, key, NULL, &p) == fail) {
        //如果找不到,那么執行插入
        s = (SearchBinaryTree)malloc(sizeof(SearchBinaryTreeNode));
        s->data = key;
        s->lChild = s->rChild = NULL;
        if (!p) {
            //在根節點就被打回來了,這時候才可能為NULL,那就是樹是一顆空樹
            //直接讓s成為根節點
            *T = s;
        } else if (key < p->data){
            //如果是比p->data小,那么把它放到p的左子樹
            p->lChild = s;
        } else if (key > p->data){
            //如果是比p->data大,那么把它放到p的右子樹
            p->rChild = s;
        }
        return success;
    } else {
        //已經有了key相同值的結點,不用再插入,
        return fail;
    }
}

3.刪除算法

刪除算法是其中最復雜的,它分為多種情況:
(1) 如果刪除的是葉子結點,那么直接將葉子結點刪除,并將葉子的雙親結點的左子樹或者右子樹指向為空即可
(2) 如果刪除的是結點只有左子樹或者右子樹,那么我們也將左子樹或者右子樹接到雙親結點即可
(3) 如果刪除的結點既有左子樹也有右子樹,這時候我們刪除后就復雜多了。我們知道,二叉排序樹經過中序遍歷后得到的是一個有序序列,那我們可以取它的直接前驅后者直接后繼就可以替換到這個位置,刪除的結點是直接前驅或直接后繼。在二叉排序樹中,要刪除結點的左子樹的最右子樹就是這個結點的直接前驅,而結點的右子樹的最左子樹就是這個結點的直接后繼。

Status deleteNode(SearchBinaryTree *node);
Status deleteBst(SearchBinaryTree *T,int key){
    if (!*T) {
        //空樹,直接返回fail
        return fail;
    }
    
    if (key == (*T)->data) {
        //找到了,就刪除掉
        return deleteNode(T);
    } else if (key < (*T)->data){
        //比值小,從左子樹上去找出來刪
        return deleteBst(&((*T)->lChild), key);
    } else if (key > (*T)->data){
        //比值大,從右子樹上去找出來刪除
        return deleteBst(&((*T)->rChild), key);
    }
    
    return fail;
}
Status deleteNode(SearchBinaryTree *node){
    //q用來標識待刪除的結點的雙親結點,s用來標識每一次迭代用到的結點
    SearchBinaryTree q,s;
    
    if ((*node)->rChild == NULL) {
        //右子樹為空,也就是只有左子樹,那么把左子樹接到node結點的雙親結點上就好了
        q = *node;
        //這一步直接把node指針存儲的值存放q的左子樹的地址,也就是接上去
        *node = q->lChild;
        //釋放結點
        free(q);
    } else if ((*node)->lChild == NULL){
        //左子樹為空,也就是只有右子樹,那么把右子樹接到node結點的雙親結點上就好了
        q = *node;
        //這一步直接把node指針存儲的值存放q的右子樹的地址,也就是接上去
        *node = q->rChild;
        free(q);
    } else {
        //左右子樹都不為空,那么我們可以用直接前驅或者直接后繼來替代該結點所在的位置
        q = *node;
        
        //下面取直接前驅或者直接后繼,直接取一個就可以了
        //一、直接前驅法
        s = (*node)->lChild;
        //直接前驅就是s最右的子樹
        while (s->rChild != NULL) {
            q = s;
            s = s->rChild;
        }
        //找到了直接前驅
        //將數據替換過去
        (*node)->data = s->data;
        if (q != *node) {
            //不相等,也就是至少進行一次遍歷,那么直接把s->lChild接過來(因為s肯定沒有右子樹的)
            q->rChild = s->lChild;
        } else {
            //找到的直接就是原有結點的左子樹(因為q->lChild沒有右子樹,沒有進行遍歷)
            //那么把s的左子樹接到結點的左子樹上
            q->lChild = s->lChild;
        }
        //釋放s結點
        free(s);
        
        //二、直接后繼法
        q = *node;
        s = (*node)->rChild;

        //直接前驅就是s最左的子樹
        while (s->lChild != NULL) {
            q = s;
            s = s->lChild;
        }
        //替換數據
        (*node)->data = s->data;
        if (q != *node) {
            //不相等,至少進行了一次遍歷
            //那么把s的右子樹接過來
            q->lChild = s->rChild;
        } else {
            //相等,就是沒有進行過遍歷
            //那么把s的右子樹接到q的右子樹上
            q->rChild = s->rChild;
        }
        free(s);
        
    }
    return success;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。