编程基础 - 二叉排序树 (Binary Search Tree)

编程基础 - 二叉排序树 (Binary Search Tree)

返回分类:全部文章 >> 基础知识

返回上级:编程基础 - 二叉树 (Binary Tree)

本文将介绍二叉排序树的基础知识,并用C++实现主要方法。

在查看本文之前,需要一些数据结构和程序语言的基础,要对“树”的概念有一些了解。

其中的方法还需要熟悉“栈(stack)”、“队列(queue)”和“递归”。


文章目录

  • 编程基础 - 二叉排序树 (Binary Search Tree)
    • 1 二叉排序树简述 (Introduction)
    • 2 二叉排序树的结构 (Structure)
    • 3 二叉排序树的查找 (Search)
    • 4 二叉排序树的插入 (Insert)
    • 5 二叉排序树的删除 (Remove)
    • 6 主函数与测试 (Main Method and Testing)
      • 6.1 主函数 (Main Method)
      • 6.2 打印结果 (Print Output)


1 二叉排序树简述 (Introduction)

二叉排序树,也称二叉查找树和二叉搜索树,它可以是空树,或同时满足如下条件:

  • 每个结点都有一个关键字(Key),做为查找依据;
  • 左子树(非空):所有结点的关键字都小于根结点;
  • 右子树(非空):所有结点的关键字都大于根结点;
  • 左子树与右子树:都是二叉排序树;
  • 对非空二叉排序树进行中序遍历,可以得到从小到大的顺序排列的关键字顺序。

注意:关键字是否可以重复,需要根据实际需求判断。

之后实例全部使用同一组数据:{ 50, 26, 78, 92, 33, 67, 22, 30, 32 }

50
26
78
22
33
67
92
30
NULL
NULL
32

2 二叉排序树的结构 (Structure)

它的结构和二叉树完全相同:

// 二叉排序树节点
template<typename T>
class BSTreeNode
{
    public:
    T element; // 数据
    BSTreeNode<T>* leftChild; // 左指针
    BSTreeNode<T>* rightChild; // 右指针

    BSTreeNode(const T& e)
    {
        element = e;
        leftChild = 0;
        rightChild = 0;
    }
    ~BSTreeNode()
    {
        leftChild = 0;
        rightChild = 0;
    }
};

3 二叉排序树的查找 (Search)

查找从根结点开始,逐层开始进行比较,直到找到目标或没有找到目标。且是一个递归的过程。

具体查找过程:

  • (1)如果根结点为空,则查找不成功;
  • (2)如果根结点不为空,进行比较:
    • 如果等于根结点,查找成功;
    • 如果小于根结点,则查找根结点的左子树;
    • 如果大于根结点,则查找根结点的右子树;
  • (3)查找成功时,指针将停留在结点;
  • (4)查找不成功时,指针将为空。

下图展示了,查找36(蓝色),查找67(红色)的过程:

67 > 50 67 < 78 36 < 50 36 > 26 36 > 33
50
26
78
22
33
67
92
30
NULL
NULL
32

C++代码:

// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree查找
// params:
//      root:       根结点
//      val:        待查找的值
//      find:       找到的结点
//      findParent: 寻找过程的上一个结点
// return:
//      bool:       查找是否成功
template<typename T>
bool BSTreeFind(BSTreeNode<T>* root, const T& val, BSTreeNode<T>*& find, BSTreeNode<T>*& findParent)
{
    if (root == 0)
    {
        return false;
    }

    find = root;
    findParent = 0;

    while (find != 0 && find->element != val) // 如果找到或未找到,退出循环
    {
        findParent = find;
        if (val < find->element) // 如果小于结点
        {
            find = find->leftChild; // 则查找结点的左子树
        }
        else // 如果大于结点
        {
            find = find->rightChild; // 则查找结点的右子树
        }
    }

    return find != 0; // 返回是否找到
}

4 二叉排序树的插入 (Insert)

假设现在的需求不允许重复关键字。

插入过程要先进行查找:

  • 如果找到结点,插入失败;
  • 如果没有找到结点,则插入。
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree插入(不可有重复Key)
// params:
//      root:       根结点
//      val:        待插入的值
// return:
//      bool:       插入是否成功(即,树中是否已存在此值,如果存在返回false)
template<typename T>
bool BSTreeInsert(BSTreeNode<T>*& root, const T& val)
{
    if (root == 0) // 如果空树
    {
        root = new BSTreeNode<T>(val); // 创建根结点
        return true;
    }

    BSTreeNode<T>* find = 0;
    BSTreeNode<T>* findParent = 0;

    if (!BSTreeFind(root, val, find, findParent)) // 如果没有找到
    {
        find = new BSTreeNode<T>(val); // 创建结点
        if (find->element > findParent->element)
        {
            findParent->rightChild = find; // 加入右子树
        }
        else
        {
            findParent->leftChild = find; // 加入左子树
        }

        return true;
    }

    return false; // 值已存在
}

5 二叉排序树的删除 (Remove)

在二叉排序树删除结点时,必须将因删除而断开的结点重新链接,这样才能保证二叉排序树的完整性。

在重新链接时,要额外注意链接位置;一般情况下,为了防止降低性能:

  • 被删除结点的右子树为空时,用它的左子树代替它;
  • 被删除结点的左子树为空时,用它的右子树代替它;
  • 被删除结点的左、右子树都不为空时,用它的右子树中序遍历的第一个结点顶替它(或用它的左子树中序遍历的最后一个结点)
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree删除
// params:
//      root:       根结点
//      val:        待插入的值
// return:
//      bool:       删除是否成功
template<typename T>
bool BSTreeDelete(BSTreeNode<T>*& root, const T& val)
{
    if (root == 0)
    {
        return false; // 树为空树
    }

    BSTreeNode<T>* find = 0;
    BSTreeNode<T>* findParent = 0;

    if (!BSTreeFind(root, val, find, findParent))
    {
        return false; // 没有找到结点
    }

    BSTreeNode<T>* linkedNode = 0;
    if (find->leftChild == 0) // 左子树空
    {
        linkedNode = find->rightChild; // 链接右子树
        find->rightChild = 0;
    }
    else if (find->rightChild == 0) // 右子树空
    {
        linkedNode = find->leftChild; // 链接左子树
        find->leftChild = 0;
    }
    else // 左右都不空,链接右子树中序遍历第一个结点
    {
        BSTreeNode<T>* tmpParent = 0; // 临时变量,父结点
        linkedNode = find->rightChild;
        while (linkedNode->leftChild != 0) // 中序遍历第一个结点
        {
            tmpParent = linkedNode;
            linkedNode = linkedNode->leftChild;
        }

        linkedNode->leftChild = find->leftChild; // 顶替,链接左子树

        // 如果 tmpParent 为空,则说明 linkedNode 就是 find->rightChild
        // 不为空,则说明中间还有结点,需要断开 linkedNode 的右子树,并链接它们
        if (tmpParent != 0 && linkedNode->rightChild != 0)
        {
            tmpParent->leftChild = linkedNode->rightChild; // 重新链接断开点
            linkedNode->rightChild = find->rightChild; // 顶替,链接右子树
        }

        find->leftChild = 0;
        find->rightChild = 0;
    }

    if (findParent != 0) // 如果删除的不是根结点
    {
        // 链接它们
        if (findParent->leftChild == find)
        {
            findParent->leftChild = linkedNode;
        }
        else
        {
            findParent->rightChild = linkedNode;
        }
    }

    delete find; // 销毁 find
    return true;
}

举例说明,我们删除一个数字26,用它右子树中序第一个结点30替代它:

  • 断开33与30,30与32的链接,并将33和32链接;

  • 断开26与22,26与33的链接,链接30与22,30与33;

  • 链接50与30。

50
26
22
33
78
67
92
30
NULL
NULL
32
  • 图 - 初始位置
断开中序第一个结点
使用30顶替26
下一步链接
断开
断开
33
30
NULL
NULL
32
30
22
33
32
NULL
  • 图 - 重新链接30断开的子树
50
30
22
33
78
67
92
32
NULL
  • 图 - 删除26之后,使用30顶替它的位置

6 主函数与测试 (Main Method and Testing)

6.1 主函数 (Main Method)

// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree

#include "binary_search_tree.h"
#include 
using namespace std;
using namespace BinaryTrees;

typedef BSTreeNode<int>* BSTree;

// 打印访问结点
void PrintVisitedBSTreeElement(BSTreeNode<int>& node)
{
    cout << node.element << ' ';
}

int main()
{
    BSTree tree = 0;

    cout << "二叉排序树:插入顺序 { 50, 26, 78, 92, 33, 67, 22, 30, 32 }" << endl;
    vector<int> vals = { 50, 26, 78, 92, 33, 67, 22, 30, 32 };
    for (int i = 0; i < vals.size(); i++)
    {
        BSTreeInsert(tree, vals[i]);
    }

    cout << "中序遍历:";
    BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
    cout << endl;

    cout << "删除 26" << endl;
    if (BSTreeDelete(tree, 26))
    {
        cout << "中序遍历:";
        BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
        cout << endl;
    }
    
    cout << "删除 33" << endl;
    if (BSTreeDelete(tree, 33))
    {
        cout << "中序遍历:";
        BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
        cout << endl;
    }

    delete tree;
    //system("pause"); VC++
    return 0;
}

6.2 打印结果 (Print Output)

二叉排序树:插入顺序 { 50, 26, 78, 92, 33, 67, 22, 30, 32 }
中序遍历:22 26 30 32 33 50 67 78 92
删除 26
中序遍历:22 30 32 33 50 67 78 92
删除 33
中序遍历:22 30 32 50 67 78 92
请按任意键继续. . .


你可能感兴趣的:(基础知识)