算法导论第十二章 二叉搜索树

一、二叉搜索树概览

  二叉搜索树(又名二叉查找树、二叉排序树)是一种可提供良好搜寻效率的树形结构,支持动态集合操作,所谓动态集合操作,就是Search、Maximum、Minimum、Insert、Delete等操作,二叉搜索树可以保证这些操作在对数时间内完成。当然,在最坏情况下,即所有节点形成一种链式树结构,则需要O(n)时间。这就说明,针对这些动态集合操作,二叉搜索树还有改进的空间,即确保最坏情况下所有操作在对数时间内完成。这样的改进结构有AVL(Adelson-Velskii-Landis) tree、RB(红黑)tree和AA-tree。AVL树和红黑树相对应用较多,我们在后面的章节中在做整理。

  在二叉搜索树中,任何一个节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树中每一个节点的键值。我们结合书本的理论对二叉搜索树的动态集合操作做编程实现。其中除了Delete操作稍稍复杂之外,其余的操作都是非常简单的。

二、二叉搜索树动态集合操作

1、查询二叉搜索树

  查询包括查找某一个元素,查找最大、最小关键字元素,查找前驱和后继,根据二叉搜索树的性质:左子树 < 根 < 右子树,这样的操作很容易实现。如下:

查找某元素:

 1 //@brief 查找元素
 2 //@return 是否查找成功
 3 bool BinarySearchTree::Search(const int search_value) const
 4 {
 5     return _Search(m_pRoot, search_value) != NULL;
 6 }
 7
 8 //@brief 真正的查找操作
 9 //非递归查找
10 BinarySearchTree::_Node * BinarySearchTree::_Search(_Node *node, const int search_value) const
11 {
12     //递归
13 //     if (node == NULL || search_value = node->m_nValue)
14 //         return node;
15 //     if (search_value < node->m_nValue)
16 //         return _Search(node->m_pLeft);
17 //     else
18 //         return _Search(node->m_pRight);
19
20     //非递归
21     while(node && search_value != node->m_nValue) {
22         if (search_value < node->m_nValue)
23             node = node->m_pLeft;
24         else
25             node = node->m_pRight;
26     }
27     return node;
28 }

查找最大、最小关键字元素:

 1 //@brief 得到以node为根节点的子树中的最大关键字节点
 2 BinarySearchTree::_Node * BinarySearchTree::_GetMaximum(_Node *node) const
 3 {
 4     while(node->m_pRight) {
 5         node = node->m_pRight;
 6     }
 7     return node;
 8 }
 9
10 //@brief 得到以node为根节点的子树中的最小关键字节点
11 BinarySearchTree::_Node * BinarySearchTree::_GetMinimum(_Node *node) const
12 {
13     while(node->m_pLeft) {
14         node = node->m_pLeft;
15     }
16     return node;
17 }

查找前驱和后继:

 1 //@brief 得到一个同时存在左右子树的节点node的前驱
 2 BinarySearchTree::_Node * BinarySearchTree::_GetPredecessor(_Node *node) const
 3 {
 4     if (node->m_pLeft)
 5         return _GetMinimum(node);
 6
 7     _Node *pTemp = node->m_pParent;
 8     while (pTemp && node == pTemp->m_pLeft) {
 9         node = pTemp;
10         pTemp = pTemp->m_pParent;
11     }
12     return pTemp;
13 }
14
15 //@brief 得到一个同时存在左右子树的节点node的后继
16 BinarySearchTree::_Node * BinarySearchTree::_GetSuccessor(_Node *node) const
17 {
18     if(node->m_pRight)
19         return _GetMaximum(node);
20
21     _Node *pTemp = node->m_pParent;
22     while (pTemp && node == pTemp->m_pRight ) {
23         node = pTemp;
24         pTemp = pTemp->m_pParent;
25     }
26     return pTemp;
27 }

2、插入和删除

插入操作:通过不断的遍历,找到待插入元素应该处的位置,即可进行插入,代码如下:包括递归和非递归的版本:

 1 //@brief 插入元素
 2 //@return 是否插入成功
 3 bool BinarySearchTree::Insert(const int new_value)
 4 {
 5     //非递归
 6     if (Search(new_value))
 7         return false;
 8
 9     _Node *pTemp = NULL;
10     _Node *pCurrent = m_pRoot;
11     while ( pCurrent ) {
12         pTemp = pCurrent;
13         if ( new_value < pCurrent->m_nValue )
14             pCurrent = pCurrent->m_pLeft;
15         else
16             pCurrent = pCurrent->m_pRight;
17
18     }
19     _Node *pInsert = new _Node();
20     pInsert->m_nValue = new_value;
21     pInsert->m_pParent = pTemp;
22
23     if ( !pTemp ) //空树
24         m_pRoot = pInsert;
25     else if ( new_value < pTemp->m_nValue )
26         pTemp->m_pLeft = pInsert;
27     else
28         pTemp->m_pRight = pInsert;
29     return true;
30 }
31
32 //递归
33 BinarySearchTree::_Node * BinarySearchTree::Insert_Recur(_Node *&node, const int new_value)
34 {
35     if ( node == NULL ) {
36         _Node *pInsert = new _Node();
37         pInsert->m_nValue = new_value;
38         node = pInsert;
39     }
40     else if ( new_value < node->m_nValue )
41         node->m_pLeft = Insert_Recur( node->m_pLeft, new_value );
42     else
43         node->m_pRight = Insert_Recur( node->m_pRight, new_value );
44     return node;
45 }

删除操作:有点小复杂,总共分为四种情况:

1)如果待删除节点没有子节点,则直接删除即可,并更改其父节点的指向;

2)如果待删除的节点只有一个子节点,其中,如果为左子树节点,则用左子树节点替换之;反之用右子树节点替换之;

3)如果待删除节点 z 有两个子节点,这个时候分两种情况考虑,第一种情况:z 的后继即为 z 的右孩子节点,则将 z 的有孩子节点替换之;

4)第二种情况: z 的后继不是 z 的有孩子节点,则首先用 z 的后继的有孩子节点替换 z 的后继,再用 z 的后继替换 z 。

没有图形作为辅助,实属难以表述和透彻理解,详细的过程和分析见书本上。

注意:在《算法导论》前几版的实现中,该过程的实现略有不同,相较上面这个过程,要简单一些,做法是:不是用 z 的后继 y 替换节点 z,而是删除节点 y,但复制 y 的关键字到节点 z 中来实现。但是这种做法,作者在最新这一版中明确提出,那种方法的缺陷是:会造成实际被删除的节点并不是被传递到删除过程中的那个节点,会有一些已删除节点的“过时”指针带来不必要的影响,总之复制删除的方法不是好的。代码实现如下:

//@brief 删除元素
//@return 是否删除成功
bool BinarySearchTree::Delete(const int delete_value)
{
    _Node *delete_node = _Search(m_pRoot, delete_value);

    //未找到该节点
    if (!delete_node)
        return false;
    else {
        _DeleteNode(delete_node);
        return true;
    }
}

//@brief 真正的删除操作
void BinarySearchTree::_DeleteNode(_Node *delete_node)
{
    if (delete_node->m_pLeft == NULL)
        _Delete_Transplant(delete_node, delete_node->m_pRight);
    else if (delete_node->m_pRight == NULL)
        _Delete_Transplant(delete_node, delete_node->m_pLeft);
    else {
        _Node *min_node = _GetMinimum(delete_node->m_pRight);
        if (min_node->m_pParent != delete_node) {
            _Delete_Transplant(min_node, min_node->m_pRight);
            min_node->m_pRight = delete_node->m_pRight;
            min_node->m_pRight->m_pParent = min_node;
        }
        _Delete_Transplant(delete_node, min_node);
        min_node->m_pLeft = delete_node->m_pLeft;
        min_node->m_pLeft->m_pParent = min_node;
    }
}

void BinarySearchTree::_Delete_Transplant(_Node *unode, _Node *vnode)
{
    if (unode->m_pParent == NULL)
        m_pRoot = vnode;
    else if (unode == unode->m_pParent->m_pLeft)
        unode->m_pParent->m_pLeft = vnode;
    else
        unode->m_pParent->m_pRight = vnode;
    if (vnode)
        vnode->m_pParent = unode->m_pParent;
}

这里增加了一个函数Transplant()来实现替换操作,使代码较简洁。

完整的代码如下:

//BinarySearchTree.h

#ifndef _BINARY_SEARCH_TREE_
#define _BINARY_SEARCH_TREE_

class BinarySearchTree
{
private:
    struct _Node {
        int        m_nValue;
        _Node    *m_pParent;
        _Node    *m_pLeft;
        _Node    *m_pRight;
    };

public:
    BinarySearchTree(_Node *pRoot = NULL):m_pRoot(pRoot){}

    ~BinarySearchTree();

    //@brief 插入元素
    //@return 是否插入成功
    bool Insert(const int new_value);

    //递归
    _Node * Insert_Recur(_Node *&node, const int new_value);

    //@brief 删除元素
    //@return 是否删除成功
    bool Delete(const int delete_value);

    //@brief 查找元素
    //@return 是否查找成功
    bool Search(const int search_value) const;

    //@brief 使用dot描述当前二叉查找树
    void Display() const;

    //@brief 树的遍历
    void Inorder() const {Inorder_Tree_Walk(m_pRoot);}
    void Preorder() const {Preorder_Tree_Walk(m_pRoot);}
    void Postorder() const {Postorder_Tree_Walk(m_pRoot);}

private:
    //@brief 真正的删除操作
    void _DeleteNode(_Node *delete_node);
    void _Delete_Transplant(_Node *unode, _Node *vnode);

    //@brief 得到以node为根节点的子树中的最大关键字节点
    _Node * _GetMaximum(_Node *node) const;

    //@brief 得到以node为根节点的子树中的最小关键字节点
    _Node * _GetMinimum(_Node *node) const;

    //@brief 得到一个同时存在左右子树的节点node的前驱
    _Node * _GetPredecessor(_Node *node) const;

    //@brief 得到一个同时存在左右子树的节点node的后继
    _Node * _GetSuccessor(_Node *node) const;

    //@brief 真正的查找操作
    //非递归查找
    _Node * _Search(_Node *node, const int search_value) const;

    //@brief 显示一棵二叉搜索树
    void _Display(/*iostream &ss, */_Node *node) const;

    //@brief 递归释放节点
    void _RecursiveReleaseNode(_Node *node);

    void ShowGraphvizViaDot(const string &dot) const;

    //@brief 树的遍历
    void Inorder_Tree_Walk(_Node *node) const;
    void Preorder_Tree_Walk(_Node *node) const;
    void Postorder_Tree_Walk(_Node *node) const;

private:
    _Node    *m_pRoot;
};

#endif//_BINARY_SEARCH_TREE_

//BinarySearchTree.cpp
#include <iostream>
#include <string>
#include <iomanip>
#include <ctime>

using namespace std;

#include "BinarySearchTree.h"

BinarySearchTree::~BinarySearchTree()
{
    _RecursiveReleaseNode(m_pRoot);
}

//@brief 插入元素
//@return 是否插入成功
bool BinarySearchTree::Insert(const int new_value)
{
    //非递归
    if (Search(new_value))
        return false;

    _Node *pTemp = NULL;
    _Node *pCurrent = m_pRoot;
    while ( pCurrent ) {
        pTemp = pCurrent;
        if ( new_value < pCurrent->m_nValue )
            pCurrent = pCurrent->m_pLeft;
        else
            pCurrent = pCurrent->m_pRight;

    }
    _Node *pInsert = new _Node();
    pInsert->m_nValue = new_value;
    pInsert->m_pParent = pTemp;

    if ( !pTemp ) //空树
        m_pRoot = pInsert;
    else if ( new_value < pTemp->m_nValue )
        pTemp->m_pLeft = pInsert;
    else
        pTemp->m_pRight = pInsert;
    return true;

//     //该元素已经存在
//     if (Search(new_value))
//         return false;
//
//     //空树,插入第1个节点
//     if (!m_pRoot) {
//         m_pRoot = new _Node();
//         m_pRoot->m_nValue = new_value;
//         return true;
//     }
//
//     //非第一个节点
//     _Node *current_node = m_pRoot;
//     while( current_node ) {
//         _Node *&next_node_pointer = (new_value < current_node->m_nValue ? current_node->m_pLeft:current_node->m_pRight);
//         if ( next_node_pointer )
//             current_node = next_node_pointer;
//         else {
//             next_node_pointer = new _Node();
//             next_node_pointer->m_nValue = new_value;
//             next_node_pointer->m_pParent = current_node;
//             break;
//         }
//     }
//     return true;
}

//递归
BinarySearchTree::_Node * BinarySearchTree::Insert_Recur(_Node *&node, const int new_value)
{
    if ( node == NULL ) {
        _Node *pInsert = new _Node();
        pInsert->m_nValue = new_value;
        node = pInsert;
    }
    else if ( new_value < node->m_nValue )
        node->m_pLeft = Insert_Recur( node->m_pLeft, new_value );
    else
        node->m_pRight = Insert_Recur( node->m_pRight, new_value );
    return node;
}

//@brief 删除元素
//@return 是否删除成功
bool BinarySearchTree::Delete(const int delete_value)
{
    _Node *delete_node = _Search(m_pRoot, delete_value);

    //未找到该节点
    if (!delete_node)
        return false;
    else {
        _DeleteNode(delete_node);
        return true;
    }
}

//@brief 查找元素
//@return 是否查找成功
bool BinarySearchTree::Search(const int search_value) const
{
    return _Search(m_pRoot, search_value) != NULL;
}

//@brief 使用dot描述当前二叉查找树
void BinarySearchTree::Display() const
{
    _Display(m_pRoot);
}

//@brief 树的遍历
//中序
void BinarySearchTree::Inorder_Tree_Walk(_Node *node) const
{
    if (node) {
        Inorder_Tree_Walk(node->m_pLeft);
        cout << node->m_nValue << " ";
        Inorder_Tree_Walk(node->m_pRight);
    }
}

//前序
void BinarySearchTree::Preorder_Tree_Walk(_Node *node) const
{
    if (node) {
        cout << node->m_nValue << " ";
        Preorder_Tree_Walk(node->m_pLeft);
        Preorder_Tree_Walk(node->m_pRight);
    }
}

//后序
void BinarySearchTree::Postorder_Tree_Walk(_Node *node) const
{
    if (node) {
        Postorder_Tree_Walk(node->m_pLeft);
        Postorder_Tree_Walk(node->m_pRight);
        cout << node->m_nValue << " ";
    }
}

//@brief 真正的删除操作
void BinarySearchTree::_DeleteNode(_Node *delete_node)
{
    if (delete_node->m_pLeft == NULL)
        _Delete_Transplant(delete_node, delete_node->m_pRight);
    else if (delete_node->m_pRight == NULL)
        _Delete_Transplant(delete_node, delete_node->m_pLeft);
    else {
        _Node *min_node = _GetMinimum(delete_node->m_pRight);
        if (min_node->m_pParent != delete_node) {
            _Delete_Transplant(min_node, min_node->m_pRight);
            min_node->m_pRight = delete_node->m_pRight;
            min_node->m_pRight->m_pParent = min_node;
        }
        _Delete_Transplant(delete_node, min_node);
        min_node->m_pLeft = delete_node->m_pLeft;
        min_node->m_pLeft->m_pParent = min_node;
    }
}

void BinarySearchTree::_Delete_Transplant(_Node *unode, _Node *vnode)
{
    if (unode->m_pParent == NULL)
        m_pRoot = vnode;
    else if (unode == unode->m_pParent->m_pLeft)
        unode->m_pParent->m_pLeft = vnode;
    else
        unode->m_pParent->m_pRight = vnode;
    if (vnode)
        vnode->m_pParent = unode->m_pParent;
}

//@brief 得到以node为根节点的子树中的最大关键字节点
BinarySearchTree::_Node * BinarySearchTree::_GetMaximum(_Node *node) const
{
    while(node->m_pRight) {
        node = node->m_pRight;
    }
    return node;
}

//@brief 得到以node为根节点的子树中的最小关键字节点
BinarySearchTree::_Node * BinarySearchTree::_GetMinimum(_Node *node) const
{
    while(node->m_pLeft) {
        node = node->m_pLeft;
    }
    return node;
}

//@brief 得到一个同时存在左右子树的节点node的前驱
BinarySearchTree::_Node * BinarySearchTree::_GetPredecessor(_Node *node) const
{
    if (node->m_pLeft)
        return _GetMinimum(node);

    _Node *pTemp = node->m_pParent;
    while (pTemp && node == pTemp->m_pLeft) {
        node = pTemp;
        pTemp = pTemp->m_pParent;
    }
    return pTemp;
}

//@brief 得到一个同时存在左右子树的节点node的后继
BinarySearchTree::_Node * BinarySearchTree::_GetSuccessor(_Node *node) const
{
    if(node->m_pRight)
        return _GetMaximum(node);

    _Node *pTemp = node->m_pParent;
    while (pTemp && node == pTemp->m_pRight ) {
        node = pTemp;
        pTemp = pTemp->m_pParent;
    }
    return pTemp;
}

//@brief 真正的查找操作
//非递归查找
BinarySearchTree::_Node * BinarySearchTree::_Search(_Node *node, const int search_value) const
{
    //递归
//     if (node == NULL || search_value = node->m_nValue)
//         return node;
//     if (search_value < node->m_nValue)
//         return _Search(node->m_pLeft);
//     else
//         return _Search(node->m_pRight);

    //非递归
    while(node && search_value != node->m_nValue) {
        if (search_value < node->m_nValue)
            node = node->m_pLeft;
        else
            node = node->m_pRight;
    }
    return node;
}

//@brief 显示一棵二叉搜索树
void BinarySearchTree::_Display(/*iostream &ss, */_Node *node) const
{
    if ( node )
    {
        cout << node->m_nValue << " ";

        if ( node->m_pLeft )
        {
            _Display( node->m_pLeft );
        }

        if ( node->m_pRight )
        {
            _Display( node->m_pRight );
        }
    }
}

//@brief 递归释放节点
void BinarySearchTree::_RecursiveReleaseNode(_Node *node)
{
    if (node) {
        _RecursiveReleaseNode(node->m_pLeft);
        _RecursiveReleaseNode(node->m_pRight);
        delete node;
    }
}

int main()
{
    srand((unsigned)time(NULL));
    BinarySearchTree bst;

    //用随机值生成一棵二叉查找树
    for (int i= 0; i < 10; i ++) {
        bst.Insert( rand() % 100 );
    }

    bst.Display();
    cout << endl;

    //中序遍历
//     bst.Inorder();
//     cout << endl;

    //删除所有的奇数值结点
    for ( int i = 1; i < 100; i += 2 )
    {
        if( bst.Delete( i ) )
        {
            cout << "### Deleted [" << i << "] ###" << endl;
        }
    }
    //验证删除的交换性
//     bst.Delete(2);
//     bst.Delete(1);
//     bst.Display();
//     cout << endl;

    //查找100以内的数,如果在二叉查找树中,则显示
    cout << endl;
    for ( int i = 0; i < 10; i += 1 )
    {
        if ( bst.Search( i ) )
        {
            cout << "搜索[" << i << "]元素:\t成功" << endl;
        }
    }

    cout << endl;
    //中序遍历
    bst.Inorder();
    return 0;
}

时间: 2024-10-10 02:20:43

算法导论第十二章 二叉搜索树的相关文章

算法导论第十五章动态规划

概述: 1.动态规划是通过组合子问题的解而解决原问题的. 2.动态规划适用于子问题不是独立的情况,也就是各子问题的包含公共的子子问题. 3.动态规划对每个子问题只求解一次,将其结果保存在一张表中. 4.动态规划的设计步骤:a.描述最优解的结构b.递归定义最优解的值c.按自底向上的方式计算最优觖的值d.由计算出的结构构造一个最优解 15.1钢条切割 钢条切割问题:给定定长的钢条和价格表,求切割方案,使得收益最大.如果n英寸的钢条的价格足够大,则不需要切割. 代码如下: //朴素递归求解钢条切割收益

算法导论第十九章 斐波那契堆

<算法导论>第二版中在讨论斐波那契堆之前还讨论了二项堆,但是第三版中已经把这块的内容放到思考题中,究极原因我想大概是二项堆只是个引子,目的是为了引出斐波那契堆,便于理解,而且许多经典的算法实现都是基于斐波那契堆,譬如计算最小生成树问题和寻找单源最短路径问题等,此时再把二项堆单独作为一章来讲显然没有必要.类似的堆结构还有很多,如左倾堆,斜堆,二项堆等,下次我打算开一篇博客来记录下它们的异同点. 一.摊还分析(第十七章) 这些高级的数据结构的性能分析一般是基于一个技术——摊还分析,可以理解成一种时

算法导论 第十六章:贪心算法之单任务调度问题

贪心算法是使所做的选择看起来都是当前最优的,通过所做的局部最优选择来产生一个全局最优解. 其具有的性质如下: 1)贪心选择性质:一个全局最优解可以通过局部最优(贪心)选择来达到.即,在考虑如何做选择时,我们只考虑对当前问题最佳的选择而不考虑子问题的结果. 这一点是贪心算法不同于动态规划之处:在动态规划中,每一步都要做出选择,但是这些选择依赖于子问题的解.因此,解动态规划问题一般是自底向上,从小问题处理至大问题.在贪心算法中,我们所做的总是当前看似最优的选择,然后再解决选择之后所出现的子问题.贪心

算法导论第十五章之钢条切割问题(自顶向下法)

#include<iostream> #include<time.h> using namespace std; #define inf -9999 int memorized_cut_rod_aux(int p[],int n,int r[]) { int q=0; if(r[n]>=0) { return r[n]; } else { //int q=inf; for(int i=1;i<=n;++i) { q=q>(p[i]+memorized_cut_ro

算法导论第十五章之钢条切割问题(自底向上版本)

#include<iostream> using namespace std; int bottom_up_cut_rod(int p[],int n,int &pos) { int *r=new int[n+1]; int *s=new int[n+1]; for(int i=0;i<=n;++i) s[i]=0; for(int i=0;i<=n;++i) r[i]=0; for(int j=1;j<=n;++j) { int q=-1; for(int i=1;

算法导论 第十九章:斐波拉契堆

斐波拉契堆是由一组最小堆有序树组成,每棵树遵循最小堆性质,并且每棵树都是有根而无序的.所有树的根通过left和right指针来形成一个环形的双链表,称为该堆的根表. 对于一个给定的斐波拉契堆H ,可以通过指向包含最小关键字的树根指针H.min来访问.堆中每个节点还包含x.mark,x.degree两个域,x.degree表示x的子女表中的子女个数:x.mark表示从x上次成为另一个节点子女以来是否失掉一个孩子. 斐波拉契对的结构如下: 势能函数: 可以利用势能方法来分析斐波拉契堆的性能.其势能函

算法导论 第十四章:数据结构的扩张

对一种数据结构的扩张过程可分为四步骤: 1)选择基础数据结构 2)确定要在基础数据结构中添加哪些信息 3)验证可用基础数据结构上的基本修改操作来维护这些新添加的信息 4)设计新的操作 动态顺序统计 动态顺序统计在红黑树的基础上添加x.size域,满足:x.size = x.left.size+x.right.size+1,扩张的结构如下: 操作: 1)检索具有给定秩的元素 下面伪代码返回一个指向以x为根的子树中包含第i小关键字的节点指针 2)确定元素的秩 伪代码如下: 3)插入 插入操作会破坏红

算法导论 第十四章:区间树

区间树是一种对动态集合进行维护的红黑树,具体设计如下: step1:基础数据结构 我们选择的基础数据结构式红黑树,其中每个节点x包含一个区间域x.int,x的关键字为区间的低端点 x.int.low. step2:附加信息 每个节点x除了区间信息外,还包含一个值x.max,即以x为根的的子树中所有区间的断点的最大值 step3:对信息维护 必须验证对含有n个节点的区间树的插入和删除都能在O(lgn)时间内完成,给定区间x.int 和 x.max 则有:x.max = max(x.int.high

算法导论之十(十一章散列表11.1-4大数组实现直接寻址方式的字典操作)

11.1-4题目: 我们希望在一个非常大的数组上,通过利用直接寻址的方式来实现一个字典.开始时,该数组中可能包含一些无用信息,但要对整个数组进行初始化是不太实际的,因为该数组的规模太大.请给出在大数组上实现直接寻址字典的方式.每个存储对象占用O(1)空间:SEARCH.INSEART.DELETE操作的时间均为O(1):并且对数据结构初始化的时间为O(1).(提示:可以利用一个附加数组,处理方式类似于栈,其大小等于实际存储在字典中的关键字数目,以帮助确定大数组中某个给定的项是否有效). 想法:

算法导论(Introduction to Algorithms )— 第十二章 二叉搜索树— 12.1 什么是二叉搜索树

搜索树数据结构支持许多动态集合操作,如search(查找).minmum(最小元素).maxmum(最大元素).predecessor(前驱).successor(后继).insert(插入).delete(删除),这些都是基本操作,可以使用一颗搜索树当做一个字典或者一个优先队列. 12.1.什么事二叉搜索树 二叉搜索树是以一棵二叉树来组织的,可以用一个链表数据结构来表示,也叫二叉排序树.二叉查找树: 其中的每个结点都是一个对象,每个对象含有多个属性(key:该结点代表的值大小,卫星数据:不清楚