二叉搜索树探究

二叉搜索树探究

完整代码:https://github.com/zzaiyuyu/BinarySearchTree

插入结点

根据二叉搜索树的定义,左孩子<根节点值<右孩子。寻找需要插入的位置,不难发现最后的位置一定是NULL,而不会存在于两个结点中间。另外用parent指针记录待插位置的父节点。

非递归:

void InsertBSTree(BSTNode** pRoot, DataType data)
{
    assert(pRoot);
    if (NULL == *pRoot) {
        //空树,改变头结点
        *pRoot = BuyNode(data);
    }
    else {
        //树不空,找到合适位置插入
        BSTNode *parent = NULL;
        BSTNode *child = *pRoot;
        //最后插入的位置一定是child为空,而不牵扯在两数中间插入
        while (child) {
            if (child->_data > data) {
                parent = child;
                child = child->_pLeft;
            }
            else {
                parent = child;
                child = child->_pRight;
            }
        }
        //找到了child的合适位置
        if (parent->_data > data) {
            parent->_pLeft = BuyNode(data);
        }
        else {
            parent->_pRight = BuyNode(data);
        }
    }
}

递归:

int InsertBSTreeR(BSTNode** pRoot, DataType data)
{
    if (NULL == *pRoot) {
        *pRoot = BuyNode(data);
        return 1;
    }
    else if ((*pRoot)->_data > data) {
        return InsertBSTreeR(&(*pRoot)->_pLeft, data);
    }
    else if ((*pRoot)->_data < data) {
        return InsertBSTreeR(&(*pRoot)->_pRight, data);
    }
    return 0;
}

查找结点

非递归:

BSTNode* FindBSTree(BSTNode* pRoot, DataType data)
{
    BSTNode *pCur = pRoot;
    while (pCur) {
        if (pCur->_data == data) {
            break;
        }
        else if (pCur->_data > data) {
            pCur = pCur->_pLeft;
        }
        else {
            pCur = pCur->_pRight;
        }
    }
    return pCur;
}

递归:

BSTNode* FindBSTreeR(BSTNode* pRoot, DataType data)
{
    if (pRoot) {
        if (data == pRoot->_data) {
            return pRoot;
        }
        else if (pRoot->_data > data) {
            return FindBSTreeR(pRoot->_pLeft, data);
        }
        else {
            return FindBSTreeR(pRoot->_pRight, data);
        }
    }
    return NULL;
}

删除结点

删除结点需要考虑的情况较多。

首先找到待删除结点的位置,然后分三种情况进行删除。

  1. 右子树为空,叶结点和非叶处理方法是一样的,而要单独考虑是不是根节点。根节点也就是没有父节点的情况。
  2. 左子树为空,同上。
  3. 左右均不空,在右子树中找到最左结点(右子树最小的),和待删结点交换值,删除这个最小结点。需要分情况讨论右子树有一个或多个结点的情况。

递归和非递归解法相似,只是寻找待删结点时可以替换。

void DeleteBSTree(BSTNode** pRoot, DataType data)
{
    assert(pRoot);
    if (*pRoot) {
            //找到要删除的结点
            BSTNode *parent = NULL;
            BSTNode *pDel = *pRoot;
            while (pDel) {
                if (pDel->_data == data) {
                    break;
                }
                else if (pDel->_data > data) {
                    parent = pDel;
                    pDel = pDel->_pLeft;
                }
                else {
                    parent = pDel;
                    pDel = pDel->_pRight;
                }
            }
            //分情况删除,右子树为空,左子树为空,左右均存在
            if (NULL == pDel) {
                return;
            }
            if (NULL == pDel->_pLeft ) {
                if (parent) {
                    if (parent->_data > pDel->_data) {
                        parent->_pLeft = pDel->_pRight;
                    }
                    else {
                        parent->_pRight = pDel->_pRight;
                    }

                }
                else {
                    //删根节点
                    *pRoot = pDel->_pRight;
                }
            }
            else if (pDel->_pRight == NULL) {
                if (parent) {
                    if (parent->_data > pDel->_data) {
                        parent->_pLeft = pDel->_pLeft;
                    }
                    else {
                        parent->_pRight = pDel->_pLeft;
                    }
                }
                else {
                    //删根节点
                    *pRoot = pDel->_pLeft;
                }
            }
            else {
                //在右子树中找最小的结点
                BSTNode *pOrg = pDel; //记录被删除的结点
                parent = pDel;
                pDel = pDel->_pRight;
                while (pDel->_pLeft) {
                    parent = pDel;
                    pDel = pDel->_pLeft;
                }
                //交换被删除结点和真正被删除的结点值
                pOrg->_data = pDel->_data;
                //删除找到的最小节点
                if (parent == pOrg) {
                    parent->_pRight = pDel->_pRight;
                }
                else {
                    parent->_pLeft = pDel->_pRight;
                }
            }
            free(pDel);
    }
}

性能

最差情况下,例如以 1为根节点的序列 1 2 3 4 5 ,形成单支树,查找的时间复杂度变为O(n)。最好的情况形成完全二叉树,时间复杂度为O(log n)。

如何解决搜索树退化为单支树失去性能优势的问题?

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度。

引入AVL树和红黑树。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页