1. 程式人生 > >(原創)像極了愛情的詳解排序二叉樹,一秒get

(原創)像極了愛情的詳解排序二叉樹,一秒get

比較 實現 每一個 方式 函數 改變 .com 元素 解讀

排序二叉樹(建立、查找、刪除)

二叉樹我們已經非常熟悉了,但是除了尋常的儲存數據、遍歷結構,我們還能用二叉樹做什麽呢?

我們都知道不同的遍歷方式會對相同的樹中產生不同的序列結果,排序二叉樹就是利用二叉樹的遍歷特征實現的特殊樹種,也叫二叉查找樹。

  1. 排序二叉樹從根結點起的每一個結點的左子樹元素均小於其自身,右子樹元素值均大於其自身
  2. 即任何結點的值均大於其左子樹所有元素,均小於其右子樹所有元素

如:技術分享圖片就是一個排序二叉樹,直觀的一批,從子樹到根結點,永遠符合左小右大的規則(中序遍歷)

Ⅰ、結構定義

排序二叉樹的定義與一般二叉樹無異

typedef struct BiTree{
    
int item; struct BiTree *lchild,*rchild; }BiTree;

Ⅱ、排序二叉樹的查找

我們先來看一下排序二叉樹的查找實現,因為插入在排序二叉樹中是實現後續建立、刪除結點的基礎,因為結點帶有順序,故而遍歷條件有所改變,代碼如下:

int searchnode(BiTree *t, int key)
{
    if(!t)
        return 0;
    if(t->item == key)
    {
        return 1;
    }
    else
    {
        if
(t->item > key) return searchnode(t->lchild,key); if(t->item < key) return searchnode(t->rchild,key); } }

清爽遞歸,不解釋

Ⅲ、二叉排序樹的插入

void insertbitree(BiTree **t, int value)
{
    if(!searchnode(*t,value))
    {
        if(*t == NULL)
        {
            
*t = (BiTree *)malloc(sizeof(BiTree)); (*t)->item = value; (*t)->lchild = NULL; (*t)->rchild = NULL; } else { if((*t)->item > value) insertbitree(&((*t)->lchild), value); else insertbitree(&((*t)->rchild), value); } } }

這個插入上來先判斷一哈我們現有的樹裏面有沒有這個元素,如果有就不會進入循環,至於插入操作的框架也基本符合中序遍歷的操作,只是加上了判斷大小

Ⅳ、二叉排序樹刪除結點(HARD)

輕松愉快的建立、查找排序二叉樹的操作完成之後,我們來看看比較困難的刪除排序二叉樹結點的操作。為什麽說它困難呢,相比插入或者查找,刪除面對的是一個已經成型的樹,我們不僅要考慮怎樣去掉這個結點,還要想到按照中序以及數字大小將原有結點按序放到正確位置。

好的,我們先來考慮一下我們可能刪除哪幾種結點:

第一類:待刪除結點只有左子樹,沒有右子樹,可以想見,這種情況下只需要把後續的左子樹接到待刪除結點的上一個結點上,再釋放待刪除結點的空間就OK

第二類:帶刪除結點只有右子樹,沒有左子樹,跟第一類一個道理,這樣的操作只需要三行就解決,但是棘手的問題總在短暫的輕松過後

第三類:這一類情況就是大魔王遼,左右孩子一個不缺,手心手背都是肉,哪個也不能少,怎麽解決這個問題呢?讓我們來看一個例子。

技術分享圖片看這個醜不拉嘰的排序二叉樹,非常體現中序遍歷特點

現在我們要刪除 34 這個結點,就是我們剛才說的那種第三類情況,左右均有結點,這個時候,我們有這兩種方法闊以達成目的

技術分享圖片

第一種:姑且叫他 犧牲前驅法 ,我們要去掉 34 ,就要把他的前驅拿來頂替這個位置,保持二叉排序樹的序,然後當然要檢測一下,如果犧牲的這個前驅點(在我們這裏是 33 )有子樹,還需要把子樹和上一級連上(如32),這是第一種方法

  1. 用直接前驅 33 替換 34
  2. 刪除原有的 33 結點
  3. 把結點 32 ,移到原 33 位置

第二種:相信你也猜到了,犧牲後繼法,反正兄弟兩個要挑一個頂上去,讓我們看一哈在這個例子中,怎麽個犧牲後繼

技術分享圖片

35 已經被我們放上來遼

  1. 用直接後繼 35 替換 34
  2. 刪除結點 35

因為這裏的 35 煢煢孑立,沒兒沒女,所以這個例子的這裏不需要連接子樹,但是千萬註意不要認為所有的替換後繼法都不用管子樹

好的,方法講明白了遼,我們代碼實現一哈

int Delete(BiTree **t)
{
    BiTree *s,*q;
    if((*t) -> lchild == NULL)//左子樹空的情況
    {
        q = *t;
        *t = (*t)->lchild;
        free(q);
    }
    else if( (*t) -> rchild == NULL)//右子樹為空的情況
    {
        q = *t;
        *t = (*t)->rchild;
        free(q);
    }
    else //左右子數均為空
    {
        q = *t;
        s = (*t)->lchild;
        while(s->rchild)//循環找到直接前驅
        {
            q = s;
            s = s->rchild;
        }
        (*t) -> item = s -> item; //結點數據替換
        if(q != *t) //接原有左右子樹
            q->rchild = s->lchild;
        else
            q->lchild = s->lchild;
        free(s);
    }
    return 1;
}
int DeleteBST(BiTree **T, int key)
{

    if (!*T)
        return 0;
    else
    {
        if (key ==  (*T)->item)
            return Delete(T);
        else if (key < (*T)->item)
            return DeleteBST(&(*T)->lchild, key);
        else
            return DeleteBST(&(*T)->rchild, key);
    }
    return 1;
}

解讀見註釋

測試用主函數部分:

int main()
{
    int i;
    BiTree *t = NULL;
    int value[] = {12,24,88,3,64,99,71,64,10,8};
    for(i = 0; i < 10; i++)
        insertbitree(&t,value[i]);
    printf("建立序列為:\n");
    lar(t);
    printf("\n");
    printf("刪除結點88,結果為:\n");
    DeleteBST(&t,88);
    lar(t);
    printf("\n");
    return 0;
}

技術分享圖片

呼~完畢

(原創)像極了愛情的詳解排序二叉樹,一秒get