1. 程式人生 > >二叉搜尋樹的基本操作 ---- 插入,刪除,查詢,銷燬,遍歷

二叉搜尋樹的基本操作 ---- 插入,刪除,查詢,銷燬,遍歷

首先來看看二叉搜尋樹的概念
二叉搜尋樹又稱二叉排序樹,它或者是一棵空樹,或者是具有以下性質的二叉樹

  • 若它的左子樹不為空,則左子樹上所有節點的值都小於根節點的值
  • 若它的右子樹不為空,則右子樹上所有節點的值都大於根節點的值
  • 它的左右子樹也分別為二叉搜尋樹

二叉搜尋樹的結構體

#define bool int
#define true 1
#define false 0
typedef int BDataType;
typedef struct BSTreeNode
{
    struct BSTreeNode* _pLeft;
    struct BSTreeNode* _pRight;
    BDataType _data;
}BSTNode;

初始化

void InitBSTree(BSTNode** pRoot)//初始化
{
    assert(pRoot);
    *pRoot = NULL;
}

插入
在二叉搜尋樹中插入新元素時,必須先檢測該元素是否在數中已經存在。如果已經存在,則不進行插入;否則將新元素加入到搜尋停止的地方。

BSTNode* BuyBSTreeNode(BDataType data)
{
    BSTNode* pNewNode = (BSTNode*)malloc(sizeof(BSTNode));
    if (pNewNode == NULL)
    {
        perror("malloc::BuyBSTreeNode"
); return NULL; } pNewNode->_data = data; pNewNode->_pLeft = NULL; pNewNode->_pRight = NULL; return pNewNode; } ////非遞迴 bool InSertBSTree(BSTNode** pRoot, BDataType data)//插入 { BSTNode* parent = NULL; BSTNode* pNewNode = NULL; BSTNode* pCur = NULL; assert(pRoot); pCur =
*pRoot; //空樹 ---- 直接插入 if (*pRoot == NULL) { *pRoot = BuyBSTreeNode(data); return true; } //找該結點在二叉搜尋樹中的插入位置 while (pCur) { parent = pCur; if (data < pCur->_data) pCur = pCur->_pLeft; else if (data > pCur->_data) pCur = pCur->_pRight; else return false; } ///插入結點 pNewNode = BuyBSTreeNode(data); if (data < parent->_data) parent->_pLeft = pNewNode; else parent->_pRight = pNewNode; return true; } /////遞迴 bool InSertBSTree(BSTNode** pRoot, BDataType data)//插入 { assert(pRoot); if (*pRoot == NULL) { *pRoot = BuyBSTreeNode(data); return true; } else { if (data == (*pRoot)->_data) return false; else if (data < (*pRoot)->_data) return InSertBSTree(&(*pRoot)->_pLeft, data); else return InSertBSTree(&(*pRoot)->_pRight, data); } }

查詢

/////////非遞迴
BSTNode* FindBSTree(BSTNode* pRoot, BDataType data)
{
    BSTNode* pCur = pRoot;
    while (pCur)
    {
        if (pCur->_data == data)
            return pCur;
        else if (data < pCur->_data)
            pCur = pCur->_pLeft;
        else
            pCur = pCur->_pRight;
    }
    return NULL;
}

/////遞迴
BSTNode* FindBSTree(BSTNode* pRoot, BDataType data)
{
    if (NULL == pRoot)
        return NULL;
    if (data == pRoot->_data)
        return pRoot;
    else if (data < pRoot->_data)
        return FindBSTree(pRoot->_pLeft, data);
    else
        return FindBSTree(pRoot->_pRight, data);
}

刪除
刪除比較複雜分為以下幾種情況
首先查詢元素是否在二叉搜尋樹中,如果不存在,則返回,否則要刪除的結點可能分以下四種情況:
1.要刪除的結點無孩子結點
2.要刪除的結點只有左孩子結點
3.要刪除的結點只有右孩子結點

情況1可以歸類到2或3
對於上述情況,相應的刪除方法如下:
1.直接刪除該結點
2.刪除該結點且使被刪除結點的雙親結點指向被刪除結點的左孩子結點
3.刪除該結點且使被刪除結點的雙親結點指向被刪除結點的右孩子結點
4.在它的左子樹中尋找中序下的第一個結點(關鍵碼最小),用它的值填補到被刪除結點中,再來處理該結點的刪除問題

///////非遞迴
bool DeleteBSTree(BSTNode** pRoot, BDataType data)//刪除
{
    BSTNode* pCur = NULL;
    BSTNode* parent = NULL;
    assert(pRoot);
    if (*pRoot == NULL)
        return;
    pCur = *pRoot;
    parent = pCur;
    while (pCur)
    {

        if (data == pCur->_data)
            break;
        else if (data < pCur->_data)
        {
            parent = pCur;          ///要指向pCur的上一個結點
            pCur = pCur->_pLeft;
        }
        else
        {
            parent = pCur;
            pCur = pCur->_pRight;
        }
    }
    ///該元素不在二叉樹中
    if (NULL == pCur)
        return false;

    ///在二叉搜尋樹中刪除pCur所指向的節點
    if (NULL == pCur->_pLeft)
    {
        //待刪除結點只有右孩子|| 葉子結點
        if (pCur == *pRoot)    //待刪除結點為根節點
            *pRoot = pCur->_pRight;
        ////pCur不為根結點
        else
        {
            if (parent->_pLeft == pCur)
                parent->_pLeft = pCur->_pRight;
            else
                parent->_pRight = pCur->_pRight;
        }

    }
    else if (NULL == pCur->_pRight)
    {
        ///待刪除節點只有左孩子
        if (pCur == *pRoot)
            *pRoot = pCur->_pLeft;
        else
        {
            if (parent->_pLeft == pCur)
                parent->_pLeft = pCur->_pLeft;
            else
                parent->_pRight = pCur->_pLeft;
        }
    }
    else
    {
        //待刪除節點左右孩子都存在
        BSTNode* pInOrder = pCur->_pRight;
        //找中序遍歷下的第一個結點(右子樹中最左側結點)
        while (pInOrder->_pLeft)
        {
            parent = pInOrder;
            pInOrder = pInOrder->_pLeft;
        }
        ///用該結點內容替換待刪除結點中資料
        pCur->_data = pInOrder->_data;

        ////刪除代替結點
        if (parent->_pLeft == pInOrder)
            parent->_pLeft = pInOrder->_pRight;
        else
            parent->_pRight = pInOrder->_pRight;
        pCur = pInOrder;
    }
    free(pCur);
    pCur = NULL;
    return true;
}


///////遞迴

bool DeleteBSTree(BSTNode** pRoot, BDataType data)//刪除
{
    assert(pRoot);
    if (*pRoot == NULL)
        return false;
    if (data < (*pRoot)->_data)
        return DeleteBSTree(&(*pRoot)->_pLeft, data);
    else if (data > (*pRoot)->_data)
        return DeleteBSTree(&(*pRoot)->_pRight, data);
    else
    {
        BSTNode* pDelNode = *pRoot;
        if (NULL == pDelNode->_pLeft)
        {
            *pRoot = pDelNode->_pRight;
            free(pDelNode);
            pDelNode = NULL;
            return true;
        }
        else if (NULL == pDelNode->_pRight)
        {
            *pRoot = pDelNode->_pLeft;
            free(pDelNode);
            pDelNode = NULL;
            return true;
        }
        else
        {
            BSTNode* pInOrder = pDelNode->_pRight;
            while (pInOrder->_pLeft)
                pInOrder = pInOrder->_pLeft;
            pDelNode->_data = pInOrder->_data; //將要刪除的結點替換
            return DeleteBSTree(&(*pRoot)->_pRight, pInOrder->_data);//去要刪除結點的右子樹中找替換後要刪除的結點,並刪除
        }
    }
}

銷燬

void DestroyBSTree(BSTNode** pRoot)//銷燬
{
    assert(pRoot);
    if (*pRoot)
    {
        DestroyBSTree(&(*pRoot)->_pLeft);
        DestroyBSTree(&(*pRoot)->_pRight);
        free(*pRoot);
        *pRoot = NULL;
    }
}

中序遍歷

void InOrder(BSTNode* pRoot)//中序遍歷
{
    if (pRoot)
    {
        InOrder(pRoot->_pLeft);
        printf("%d   ", pRoot->_data);
        InOrder(pRoot->_pRight);
    }
}