- 作者:邹祁峰
- 邮箱:Qifeng.zou.job@gmail.com
- 博客:http://blog.csdn.net/qifengzou
- 日期:2013.12.27 16:45
- 转载请注明来自”祁峰“的CSDN博客
1 引言
博文《算法导论 之 平衡二叉树 – 打印》中使用递归算法实现了平衡二叉树的打印功能,仿照此博文中的代码可快速实现红黑树打印和销毁的递归算法。虽然递归方式比较简单明了,但和非递归算法比起来,其效率偏低。为了提高对红黑树的处理效率,在此使用非递归算法实现对红黑树的打印和销毁功能,今后的二叉树算法的打印和销毁都可以仿照以下代码进行实现。对于该篇中出现的相关的数据结构、宏、枚举以及函数,可以在博文《通用栈的设计和实现》中找到定义和实现。[注:二叉树的处理都可以仿照此代码进行遍历和打印]
2 代码实现
因实现过程不太好用图形来表示,因此,直接在此给出源码。如果你不太理解其实现原理,请自己绘制堆栈图来慢慢理解。
2.1 打印过程
/******************************************************************************
**函数名称: rbt_print
**功 能: 打印红黑树(外部接口)
**输入参数:
** tree: 红黑树
**输出参数: NONE
**返 回: VOID
**实现描述:
**注意事项:
**作 者: # Qifeng.zou # 2013.12.27 #
******************************************************************************/
int rbt_print(const rbt_tree_t *tree)
{
int depth = 0;
Stack_t _stack, *stack = &_stack;
const rbt_node_t *node = tree->root, *parent = NULL;
if(tree->sentinel == node) return 0;
stack_init(stack, RBT_MAX_DEPTH);
while(tree->sentinel != node)
{
/* 压左孩子入栈 */
while(tree->sentinel != node->lchild)
{
rbt_assert(tree, node);
depth = stack_depth(stack);
stack_push(stack, node);
rbt_hprint(tree, node, depth); /* 打印头:入栈时打印头 出栈时打印尾 */
node = node->lchild;
}
/* 打印最左端的子孙结点 */
depth = stack_depth(stack);
rb_hprint(tree, node, depth);
/* 最左端的孩子有右孩子 */
if(tree->sentinel != node->rchild)
{
stack_push(stack, node);
node = node->rchild;
continue;
}
/* 最左端的孩子无右孩子 */
rbt_tprint(tree, node, depth);
parent = stack_gettop(stack);
if(NULL == parent)
{
return stack_destory(stack);
}
/* 判断最左结点的父结点未处理完成 */
if((parent->lchild == node)
&& (tree->sentinel != parent->rchild))
{
node = parent->rchild;
continue;
}
/* 判断最左结点的父结点已处理完成 */
while((node == parent->rchild)
|| (tree->sentinel == parent->rchild))
{
stack_pop(stack);
depth = stack_depth(stack);
rbt_tprint(tree, parent, depth); /* 打印尾:出栈时打印尾 入栈时已打印头 */
node = parent;
parent = stack_gettop(stack);
if(NULL == parent)
{
return stack_destory(stack);
}
}
node = parent->rchild;
}
return stack_destory(stack);
}
代码1 非递归算法
2.2 打印头部
/******************************************************************************
**函数名称: rbt_hprint
**功 能: 打印结点头(内部接口)
**输入参数:
** node: 被打印的结点
** depth: 结点深度
**输出参数: NONE
**返 回: VOID
**实现描述:
**注意事项:
**作 者: # Qifeng.zou # 2013.12.17 #
******************************************************************************/
static void rbt_hprint(const rbt_tree_t *tree, const rbt_node_t *node, int depth)
{
int idx = 0;
rbt_node_t *parent = node->parent;
while(depth > 0 && (NULL != parent))
{
if(1 == depth)
{
fprintf(stderr, "|");
for(idx=0; idx<8; idx++)
{
if(0 == idx)
{
if(parent->lchild == node)
{
fprintf(stderr, "l");
}
else
{
fprintf(stderr, "r");
}
}
else
{
fprintf(stderr, "-");
}
}
}
else
{
fprintf(stderr, "|");
for(idx=0; idx<8; idx++)
{
fprintf(stderr, " ");
}
}
depth--;
}
if((tree->sentinel == node->lchild)
&& (tree->sentinel == node->rchild))
{
fprintf(stderr, "<%03d:%c/>\n", node->key, node->color);
}
else
{
fprintf(stderr, "<%03d:%c>\n", node->key, node->color);
}
}
代码2 打印头部
2.3 打印尾部
/******************************************************************************
**函数名称: rbt_hprint
**功 能: 打印结点头(内部接口)
**输入参数:
** node: 被打印的结点
** depth: 结点深度
**输出参数: NONE
**返 回: VOID
**实现描述:
**注意事项:
**作 者: # Qifeng.zou # 2013.12.17 #
******************************************************************************/
static void rbt_hprint(const rbt_tree_t *tree, const rbt_node_t *node, int depth)
{
int idx = 0;
rbt_node_t *parent = node->parent;
while(depth > 0 && (NULL != parent))
{
if(1 == depth)
{
fprintf(stderr, "|");
for(idx=0; idx<8; idx++)
{
if(0 == idx)
{
if(parent->lchild == node)
{
fprintf(stderr, "l");
}
else
{
fprintf(stderr, "r");
}
}
else
{
fprintf(stderr, "-");
}
}
}
else
{
fprintf(stderr, "|");
for(idx=0; idx<8; idx++)
{
fprintf(stderr, " ");
}
}
depth--;
}
if((tree->sentinel == node->lchild)
&& (tree->sentinel == node->rchild))
{
fprintf(stderr, "<%03d:%c/>\n", node->key, node->color);
}
else
{
fprintf(stderr, "<%03d:%c>\n", node->key, node->color);
}
}
代码3 打印尾部
2.4 销毁过程
销毁结果要求释放树中每个结点所占用的内存空间,不允许存在内存泄露的现象。非递归算法销毁红黑树的过程与非递归打印红黑树的过程非常相似,不过有如下几点需要注意:
①、左右子树均为叶子结点在被判断是否为其父结点的左右孩子后,便可直接释放
②、其他树内结点在出栈时才释放空间
③、叶子结点最后释放
/******************************************************************************
**函数名称: rbt_destory
**功 能: 销毁红黑树(外部接口)
**输入参数:
** tree: 红黑树
**输出参数: NONE
**返 回: VOID
**实现描述:
** 1. 左右孩子均为叶子结点的结点在判断是否为其父结点的左右孩子后便可直接释放
** 2. 其他树内结点出栈时才释放
** 3. 叶子结点最后释放
**注意事项:
**作 者: # Qifeng.zou # 2013.12.27 #
******************************************************************************/
int rbt_destory(rbt_tree_t **tree)
{
Stack_t _stack, *stack = &_stack;
rbt_node_t *node = (*tree)->root, *parent = NULL, *top = NULL;
if((*tree)->sentinel == node) return 0;
stack_init(stack, RBT_MAX_DEPTH);
while((*tree)->sentinel != node)
{
/* 压左孩子入栈 */
while((*tree)->sentinel != node->lchild)
{
stack_push(stack, node);
node = node->lchild;
}
/* 最左端的孩子有右孩子 */
if((*tree)->sentinel != node->rchild)
{
stack_push(stack, node);
node = node->rchild;
continue;
}
parent = stack_gettop(stack);
if(NULL == parent)
{
free(node);
free((*tree)->sentinel);
free(*tree), *tree = NULL;
stack_destory(stack);
return RBT_SUCCESS;
}
if((parent->lchild == node) /* 右孩子是否已处理 */
&& ((*tree)->sentinel != parent->rchild))
{
free(node);
node = parent->rchild;
continue;
}
/* 其他树内结点出栈时释放 */
while((node == parent->rchild)
|| ((*tree)->sentinel == parent->rchild))
{
stack_pop(stack);
free(node); /* 出栈结点下一次循环时释放 */
node = parent;
parent = stack_gettop(stack);
if(NULL == parent)
{
free(node);
free((*tree)->sentinel);
free(*tree), *tree = NULL;
stack_destory(stack);
return RBT_SUCCESS;
}
}
if(NULL != node) /* 释放上面出栈的结点 */
{
free(node);
}
node = parent->rchild;
}
free((*tree)->sentinel);
free(*tree), *tree = NULL;
stack_destory(stack);
return RBT_SUCCESS;
}
代码4 销毁红黑树
3 运行结果
随机输入20个不同的关键字,并打印对应的红黑树,其结构显示如下:
图1 红黑树结构