基本数据结构 -- 二叉查找树的插入、删除、查找和遍历

1、什么是二叉查找树算法

  二叉查找树(Binary Search Tree)是一种特殊的二叉树,对于一个二叉查找树,树中的每一个结点X,它的左子树中全部关键字的值都小于X的关键字值;而它的右子树中全部关键字的值大于X的关键字值。这意味着,该树的全部元素可使用一种统一的方式进行排序,所以,二叉查找树又称为二叉排序树。下图即为一个二叉查找树:数据结构

2、如何在 BST 中查找一个结点spa

  二叉查找树很适合进行查找操做。以上图中的二叉查找树为例:若是须要在该 BST 中查找值 10,先从根结点开始进行比较,10小于13,根据 BST 的性质,能够知道若是该 BST 中有结点值为10的话,那么该结点必然位于根结点的左子树中;再从结点 5 开始,可知 10 在结点 5 的右子树;再从结点 8 开始,10 在结点 8 的右子树,最终找到元素 10。code

  用算法来描述,在一个 二叉查找树 T(T 为二叉树的根结点)中查找元素 n:blog

1)若 T 为空,则 n 不在 BST 中;排序

2)判断 n 与根结点值(T->val)的关系;递归

3)若是 n 等于 T->val,则找到了元素 n;内存

4)若是 n 大于 T->val,则去根结点的右子树 (T->right) 继续查找。将 T->right 做为 T,回到第 1 步;element

5)若是 n 小于 T->val,则去根结点的左子树 (T->left ) 继续查找。将 T->left 做为 T,回到第1步。博客

  算法实现以下:

typedef int ElementType;

typedef struct BinaryTreeNode_ {
    ElementType val;
    struct BinaryTreeNode_ *left;
    struct BinaryTreeNode_ *right;
}BinaryTreeNode;

typedef BinaryTreeNode *BiTreeNode;

// 查找一个结点
BiTreeNode FindNode(BiTreeNode BiTree,ElementType n)
{
    if (BiTree == NULL)        // 若是头结点为空,代表没有找到元素 n
        return NULL;
    if (BiTree->val < n)    // 结点值小于查找元素值,说明元素在结点的右边
        return FindNode(BiTree->right, n);    // 递归,去右子树继续查找
    else if (BiTree->val > n)
        return FindNode(BiTree->left, n);    // 递归,去左子树继续查找
    else
        return BiTree;
} 

   该查找算法的时间复杂度为 O(log2N)。

 

3、向 BST 中插入一个结点

  向 BST 中插入一个结点 n 时,若 BST 中已经存在与该结点值相等的结点,则判断结点已存在,不作任何操做;若是 BST 中不存在该结点,则将该结点将做为一个新的叶子结点插入到 BST 中去(新结点老是 BST 的叶子结点),且须要保证插入后的树是一个二叉查找树。

  能够以相似查找算法的方式来遍历二叉树,从而找到插入的位置:

  用算法来描述,向一个二叉查找树 T(T 为二叉树的根结点)中插入一个结点,节点元素为 n:

1)若 T 为空,则将新结点插入到 T 所在的位置;

2)若 T 不为空,且 T->val 小于 n,则代表应该把 n 插入到 T 的右子树。将 T->right 做为 T,回到第 1 步;

3)若 T 不为空,且 T->val 大于 n,则代表应该把 n 插入到 T 的左子树。将 T->left 做为 T,回到第 1 步。

4)若 T->val 等于 n,则直接返回 T。

// 插入一个结点
BiTreeNode InsertNode(BiTreeNode BiTree, ElementType n)
{
    if (BiTree == NULL) {    // 树为空,或已到达叶子结点
        // 为新结点分配内存
        BiTreeNode newNode = (BiTreeNode)malloc(sizeof(BinaryTreeNode));
        if (newNode == NULL) {
            perror("malloc failed!\n");
            exit(EXIT_FAILURE);
        }
        newNode->val = n;
        newNode->left = NULL;
        newNode->right = NULL;
        BiTree = newNode;
    }
    else if (BiTree->val < n) {    // 插入值大于当前结点的值,则将元素插入到结点的右子树
        BiTree->right = InsertNode(BiTree->right, n);
    }
    else if (BiTree->val > n) {    // 插入值小于当前结点的值,则将元素插入到结点的左子树
        BiTree->left = InsertNode(BiTree->left, n);
    }
    
    return BiTree;
    
}

 

4、遍历 BST

  在上一篇博客中已经介绍了二叉树的遍历方法,主要有三种:先序遍历、后序遍历和中序遍历。为了更好的体现二叉查找树的特性,咱们使用中序遍从来遍历它:

// 中序遍历 BST
void TraverseTree(BiTreeNode BiTree)
{
    if (BiTree != NULL) {
        TraverseTree(BiTree->left);
        cout << BiTree->val << endl;
        TraverseTree(BiTree->right);
    }

}

 

5、删除一个结点

  删除操做比较复杂,分为三种状况:

1)被删除结点为叶子结点:

  能够直接删除该结点,如图:

2)被删除结点有一个左孩子或一个右孩子:

  将孩子结点设为该结点的父结点的孩子后,便可删除该结点:

  如图,结点 2 是结点 5 的左孩子,他有一个右孩子 4。删除结点 2 后,其右孩子 4 替代原来的结点 2 成为结点 5 的左孩子。

  如图,结点 16 是结点 18 的左孩子,他有一个左孩子 15。删除结点 16 后,其左孩子 15 替代原来的结点 16 成为结点 18 的左孩子。

3)被删除结点有两个孩子结点:

  这种状况比较复杂,通常的删除策略是:用被删除结点的右子树中的最小结点替代被删除结点,并递归地删除这个最小数据结点:

// 删除一个结点
BiTreeNode DeleteNode(BiTreeNode BiTree, ElementType n)
{
    BiTreeNode tmpNode = (BiTreeNode)malloc(sizeof(BinaryTreeNode));
    if (tmpNode == NULL) {
        perror("malloc failed!\n");
        exit(EXIT_FAILURE);
    }

    // 先找到要删除的结点在二叉树中的位置
    if (BiTree == NULL) {    // 没有找到元素
        perror("can`t find element\n");
        return NULL;
    }
        
    if (BiTree->val < n) {        // 元素值大于结点元素值,则去结点右子树寻找
        BiTree->right = DeleteNode(BiTree->right, n);
        return BiTree;
    }
    else if (BiTree->val > n) {    // 元素值小于结点元素值,则去结点左子树寻找
        BiTree->left = DeleteNode(BiTree->left, n);
        return BiTree;
    }
    // 找到结点
    else {
        tmpNode = BiTree;
        if (BiTree->right == NULL) {        // 没有右子树,则直接返回左子树
            BiTree = BiTree->left;
            return BiTree;
        }
        else if (BiTree->left == NULL) {    // 没有左子树,则直接返回右子树
            BiTree = BiTree->right;
            return BiTree;
        }
        // 有两个孩子结点
        tmpNode = FindMin(BiTree->right);    // 寻找右子树最小结点
        tmpNode->right = DeleteMin(BiTree->right);    // 删除右子树的最小结点
        tmpNode->left = BiTree->left;        // 左子树保持不变
        return tmpNode;
    }
}

// 查找最左叶子结点(最小的结点)
BiTreeNode FindMin(BiTreeNode BiTree)
{
    if (BiTree == NULL)
        return NULL;
    if (BiTree->left == NULL)    // 结点没有左子树,即为最左叶子结点
        return BiTree;
    else        // 有左子树,则继续在左子树中查找
        return FindMin(BiTree->left);
}

// 删除最小结点
BiTreeNode DeleteMin(BiTreeNode BiTree)
{
    if (BiTree->left == NULL)
        return BiTree->right;
    else {
        BiTree->left = DeleteMin(BiTree->left);
        return BiTree;
    }

}

 

 

参考资料:

《数据结构与算法分析 C 语言描述》

相关文章
相关标签/搜索