Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150758
  • 博文数量: 54
  • 博客积分: 1732
  • 博客等级: 上尉
  • 技术积分: 520
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-23 23:29
文章分类

全部博文(54)

文章存档

2011年(3)

2010年(26)

2009年(25)

分类: C/C++

2009-12-21 23:56:00


三、BinTree的实现
1、数据成员
按照上面的设计,BinTree至少需要三个数据成员:
    Node    * root;
    size_t        numOfElem;
    Compare        comp;

2、构造函数
默认构造函数构造一颗空的二叉树:

explicit BinTree(const Compare & srcComp = Compare())
   : root(0), numOfElem(0), comp(srcComp){}    ;

注意我这里使用了explicit关键字,是为了杜绝从Compare类型到BinTree类型的隐式转换,因为这显然不会是我们想要的

3、复制控制
显而易见的,对于二叉树的复制控制,采用递归是最方便易懂的方法,因此可以定义以下辅助函数

/*
 * 以subRoot为根节点,复制srcTree
 * 注意本函数不负责清理subRoot原来指向的资源,
 * 也不负责numOfElem,comp等对象的调整
 */
template
void BinTree::                /* return type and scope */
sub_Tree_Init(const Node * subRoot,
           const Node * srcTreeRoot)
{
    if( 0 == srcTreeRoot)
        return subRoot = 0;

    subRoot = new Node( *srcTreeRoot, comp );
    sub_Tree_Init(subRoot->left, srcTreeRoot->left );
    sub_Tree_Init(subRoot->right, srcTreeRoot->right);

    return;
}

/* 删除以subRoot 为根节点的 subTree
 * 同样的,本函数不负责numOfElem, comp等对象的调整
 */
template
void BinTree::        /* return type and scope */
sub_Tree_Del(const Node *subRoot)
{
    if (subRoot)
    {
        sub_Tree_Del(subRoot->left);
        sub_Tree_Del(subRoot->right);
        delete subRoot;
    }
    return;
}

到这里,三个复制控制函数只需要负责numOfElem,comp等处理就行了



4、size()
这个是最简单的,直接return numOfElem即可

5、min(), max()
只要理解了二叉树的性质,这个也不难,最左边的节点就是最小节点,最右边的节点为最大节点,min()可以用类似下面的代码实现:

    Node * node;
    for( node = subRoot; 0 != node->left;  node = node->left)
        ;
    return node->data;

6、query()
可以用类似二分查找的方法实现,不过在二叉树里进行二分查找远比在数组中做二分查找来得简单。唯一需要注意的是,因为这里采用模板实现,所以必须要采用二元函数对象(binary functor)Compare来定义比较操作,而不是通常所用的小于号("<")

template
Node * BinTree::
search(const Data& data) const
{
    Node * node = root;
    Node * pare = 0;       /* record the parent of node */
    while( 0 != node)
    {
        pare = node;
        if( comp(node->data, data) )            /* 节点上的数据较小 */
            node = node->right;
        else if( comp(data, node->data) )        /* 节点上的数据较大 */
            node = node->left;
        else                            /* 二者相等 */
            return node;                
    }
    /* 搜索不成功,返回插入该data时其所应该在的节点的父节点 */
    return pare;
}/* search()*/

在这里,我并没有直接定义query函数,而是定义了一个search函数,这个search函数的行为是:如果在二叉树中找到数据为data的节点,则返回该节点,如果没有找到数据为data的节点,则返回(向二叉树中插入data时其所应该在的节点的)父节点
有了这个search函数,结合Node类定义的operator==()函数,可以实现query()函数如下:

bool    query(const Data& data ) const
    {Node* node  = search(data);
        if(0 != node) return  *node == data; return false;  }

因为对任何类型Type,*(static_cast(0))都会引起运行时错误,所以在返回前需要检验node是否为0

7、successor()
在successor函数体中,首先调用search函数找到data所在的节点的位置
(1)、如果在二叉树中存在数据为data的节点x,则可以对其做分析如下:
    A、如果该节点x存在右子节点,则successor为其右子树上的最小值
    B、如果该节点x没有右子节点,且该树中存在节点x的后继(successor)节点y,则节点y 满足以下条件: a) y是的左子节点是x的祖先节点(这里的祖先节点包括自己及其父节点),b) y是满足条件a)的最低节点……不知道表述清楚没有,还是看伪代码好了
具体证明请参见《算法导论》,实际上,画个图就出来了

TREE_SUCCESSOR(x)
    if(right[x]) != NULL
        return TREE-MIN( right[x] )

    y = parent[x]
    while y != NULL && x=right[y] {
        x=y
        y=parent[y]
    }
    return y

(2)如果二叉树中不存在数据为data的节点x,比较data与search函数所返回节点中的数据值,结合情况(1)的讨论,很快就能得到相似的代码,这里不再赘述

注:写到这里的时候,我发现了已经初步测试通过的BinTree隐藏的一个bug: 与min()、max()一样,successor()也需要处理所查询的数据不存在后继的情况(data比二叉树中的所有数据都大),这时候,该返回什么?在我现有的程序中,这里会产生一个运行时错误(段错误),懒得去改了,交给下一个版本吧。


8、insert()、remove()函数
因为已经有了search函数,insert函数的实现就比较简单了,将节点与节点用指针连起来即可,需要考虑树为空以及待插入的数据已经存在与树中两种特殊情况。
remove的情况比较复杂,首先当然是以search函数查找待删除的数据所在的节点,如果该数据压根就不存在于二叉树中,那当然是返回false了事,如果存在的话,可以三种情况讨论:
1) 待删除的节点没有子节点,直接将其父节点相应的指针置零,然后回收待删除的节点所占的资源
2) 待删除的节点(设为节点x)有一个子节点,将x的子节点的parent指针指向x的父节点,然后将x的父节点相应的子指针(left或者right)指向x的子节点,最后回收资源
3) 待删除的节点(设为节点x)有两个子节点,我们的做法是删除x的successor节点,然后将所删除的节点中的数据复制到节点x中,这样就等效于删除了x节点,而且保持了二叉树的性质不变(即左边的节点都小于右边的节点),最后回收所删除的节点所占的资源。

最后,show一下我的remove函数的代码:

template
bool BinTree::        /* return type and scope */
remove(const Data& data)
{
    Node *node = search(data);
    if(*node == data)        /* 主体代码 */
    {
        /* 指定待删除的节点 */
        Node * del;
        if(0 == node->left || 0 == node->right)
            del = node;
        else
            del = sub_Tree_Min(node->left);

        /*删除*/
        Node * child_Of_Del;
        if( 0 != del->left )
            child_Of_Del = del->left;
        else
            child_Of_Del = del->right;
        /* 将del的子节点的parent指针指向del的父节点,
         * 注意这里del是不可能有两个子节点的*/
        if( 0 != child_Of_Del)        /* del节点有一个子节点 */
            child_Of_Del->parent = del->parent;
        /* 将del的父节点对应的子指针(left 或者 right)指向del对应的子节点 */
        if( 0 == del->parent)
            root = child_Of_Del;
        else if ( (del->parent)->left ==del)
            (del->parent)->left = child_Of_Del;
        else
            (del->parent)->right = child_Of_Del;

        /*
         * 处理del != node 的情况
         */
        if( del != node )
            node->data = del->data;

        delete del;
        --numOfElem;
        return true;
    }/* 删除指定数据结束 */

    /* 树中不存在数据为data的节点 */
    return false;
}/* remove() */

上面的代码没有完全按照前面的分析进行,而是综合了三种情况直接进行控制

阅读(614) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~