1. 程式人生 > >二叉搜尋樹的查詢、插入、刪除操作

二叉搜尋樹的查詢、插入、刪除操作

定義: 二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別為二叉排序樹; (4)沒有鍵值相等的結點

對於查詢和插入操作相對來說比較簡單,刪除操作則需要考慮的情況較多。

比如有以下些情況,可能有些可以合併,但是我沒想到更好的方法,因此程式碼裡有一堆if else,雖然我不喜歡寫這樣的程式碼,但也沒找到更好的方法。

1. 考慮是否是空樹、或者需要刪除的結點不在樹中。

2. 考慮整棵樹只有一個結點,刪除後需要修改入參,將整棵樹置空。

3. 考慮刪除的結點是葉子結點。

4. 待刪除的結點只有左子樹,或者只有右子樹。

5.待刪除的結點有左右子樹。

下面是我的程式碼,簡單測試過不同的用例,沒什麼問題,但是由於一堆if  else讓我很不放心,還有問題也說不定。

//bsearchtree.h

#include <stdio.h>

typedef struct Item{
    int m_item;
    /**/
}Item;

typedef struct Node{
    int key;
    Item item;
}Node;

typedef struct Tree{
    Node node;
    struct Tree * lchild;
    struct Tree * rchild;
}Tree;

typedef struct Tree * TreePointer;

const Tree * mybsearch(const Tree * tree, int key);
int binsert(TreePointer * ptree, const Node * nd);
int bdelete(TreePointer * ptree, int key);
void threeWayJoin(Tree * small, Tree * mid, Tree * big);
void twoWayJoin(Tree * small, Tree * big);
void split(Tree * tree, int k, Tree * small, Tree * mid, Tree * big);
void show(TreePointer tree);

//bsertchtree.c

#include "bsearchtree.h"
#include <stdlib.h>

const Tree * mybsearch(const Tree * tree, int key)
{
    if(NULL == tree)
    {
        return NULL;
    }
    if(key == tree->node.key)
        return tree;
    if(key > tree->node.key)
        return mybsearch(tree->rchild, key);
    else
        return mybsearch(tree->lchild, key);
}
/*迴圈實現
{
    while(tree)
    {
        if(key == tree->node.key)
            return tree;
        if(key > tree->node.key)
        {
            tree = tree->rchild;
        }
        else
        {
            tree = tree->lchild;
        }
    }
    return NULL;
}
*/
int binsert(TreePointer * ptree, const Node * nd)
{
    Tree * current = *ptree;
    Tree * prev = NULL;
    Tree * pnew;
    
    while(current)
    {
        if(nd->key == current->node.key)
        {
            return 0;
        }
        prev = current;
        if(nd->key > current->node.key)
            current = current->rchild;
        else
            current = current->lchild;
    }
    
    pnew = (Tree *)malloc(sizeof(Tree));
    pnew->node = *nd;
    pnew->lchild = pnew->rchild = NULL;
    
    if(prev)
    {
        if(nd->key > prev->node.key)
            prev->rchild = pnew;
        else
            prev->lchild = pnew;
    }
    else
        *ptree = pnew;
    
    return 1;
}

int bdelete(TreePointer * ptree, int key)
{
    Tree * current = *ptree;
    Tree * prev = NULL;
    int flag = 0;//找到關鍵字為1, 沒找到為0
    
    /*查詢樹中是否存在關鍵字項key, current 指向含key的結點, prev指向其前驅結點*/
    while(current)
    {
        if(key == current->node.key)
        {
            flag = 1;
            break;
        }
        prev = current;
        if(key > current->node.key)
            current = current->rchild;
        else
            current = current->lchild;
    }
    //沒有查詢到需要刪除的結點
    if(!flag)
    {
        return 0;
    }
    //整棵樹只有一個結點
    if((*ptree)->lchild == NULL && (*ptree)->rchild == NULL)
    {
        free(*ptree);
        *ptree = NULL;
        return 1;
    }
    //待刪除的結點是葉子結點
    if(current->lchild == NULL && current->rchild == NULL)
    {
        if(prev->lchild == current)
            prev->lchild = NULL;
        else
            prev->rchild = NULL;
    }
    //待刪除的結點只有一個右子結點
    else if(current->lchild == NULL)
    {
        if(prev->lchild == current)//前驅結點的左指標指向當前結點
            prev->lchild = current->rchild;
        else //前驅結點的右指標指向當前結點
            prev->rchild = current->rchild;
    }
    //待刪除的結點只有一個左子結點
    else if(current->rchild == NULL)
    {
        if(prev->lchild == current)
            prev->lchild = current->lchild;
        else
            prev->rchild = current->lchild;
    }
    //待刪除的結點有2個子結點
    else
    {
        //尋找左子樹中key最大的結點, save儲存其前驅結點
        Tree * save = current;
        Tree * temp = current->lchild;
        
        while(temp->rchild)
        {
            save = temp;
            temp = temp->rchild;
        }
        if(save == current)
            save->lchild = temp->lchild;
        else
            save->rchild = temp->lchild;
        current->node = temp->node;
        current = temp;
    }
    free(current);
    return 1;
}

void threeWayJoin(Tree * small, Tree * mid, Tree * big);
void twoWayJoin(Tree * small, Tree * big);
void split(Tree * tree, int k, Tree * small, Tree * mid, Tree * big);
void show(TreePointer tree)
{
    if(tree)
    {
        printf("key = %d, data = %d\n", tree->node.key, tree->node.item.m_item);
        show(tree->lchild);
        show(tree->rchild);
    }
}

//test.c  ,測試的資料可以自己修改

#include "bsearchtree.h"
#include <stdlib.h>
#include <time.h>


int main(void)
{
    int i;
    Node node;
    TreePointer tree;
    
    srand(time(NULL));
    tree = NULL;
    
    /*
    for(i =0; i < 10; i++)
    {
        node.key = rand() % 10;
        node.item.m_item = i;
        binsert(&tree, &node);
        printf("insert: key = %d, data = %d\n", node.key, node.item.m_item);
    }
    */
    node.key = 1;
    node.item.m_item = 0;
    binsert(&tree, &node);
    
    node.key = 8;
    binsert(&tree, &node);
    
    node.key = 3;
    binsert(&tree, &node);
    
    node.key = 2;
    binsert(&tree, &node);
    
    
    node.key = 9;
    binsert(&tree, &node);
    
    printf("after insert: \n");
    show(tree);
    printf("after delete: \n");
    bdelete(&tree, 8);
    show(tree);
    
    return 0;
}