前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作二叉树插入二叉树删除1删除2删除3。但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么办呢?按照现在的结构,我们只能一个一个数据查找验证,首先看看在不在排序二叉树中,如果在那么删除;如果没有这个数据,那么继续查找。那么有没有方法,可以保存当前节点的下一个节点是什么呢?这样就不再需要进行无谓的查找了。其实这样的方法是存在的,那就是在排序二叉树中添加向前向后双向节点。
    现在数据结构定义如下:

typedef struct _TREE_NODE
{
int data;
struct _TREE_NODE* prev;
struct _TREE_NODE* next;
struct _TREE_NODE* left;
struct _TREE_NODE* right;
}TREE_NODE;

拿节点的添加来说,我们可能需要添加prev、next的处理步骤。

void set_link_for_insert(TREE_NODE* pParent, TREE_NODE* pNode)
{
if(NULL == pParent || NULL == pNode)
return; if(pNode = pParent->left){
pNode->prev = pParent->prev;
if(pParent->prev)
pParent->prev->next = pNode;
pNode->next = pParent;
pParent->prev = pNode;
}else{
pNode->next = pParent->next;
if(pParent->next)
pParent->next->prev = pNode;
pNode->prev = pParent;
pParent->next = pNode;
} return;
} STATUS add_node_into_tree(TREE_NODE** ppTreeNode, int data)
{
TREE_NODE* pHead;
TREE_NODE* pNode; if(NULL == ppTreeNode)
return FALSE; if(NULL == *ppTreeNode){
*ppTreeNode = create_new_node(data);
return TRUE;
} if(NULL != find_data_in_tree(*ppTreeNode, data))
return FALSE; pHead = *ppTreeNode;
while(1){
if(data < pHead->data){
if(pHead->left){
pHead = pHead->left;
}else{
pNode = create_new_node(data);
pHead->left = pNode;
break;
}
}else{
if(pHead->right){
pHead = pHead->right;
}else{
pNode = create_new_node(data);
pHead->right = pNode;
break;
}
}
} set_link_for_insert(pHead, pNode);
return TRUE;
}

添加节点如此,删除节点的工作也不能马虎。

void set_link_for_delete(TREE_NODE* pNode)
{
if(pNode->prev){
if(pNode->next){
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
}else
pNode->prev->next = NULL;
}else{
if(pNode->next)
pNode->next->prev = NULL;
}
} TREE_NODE* _delete_node_from_tree(TREE_NODE* root, TREE_NODE* pNode)
{
TREE_NODE* pLeftMax;
TREE_NODE* pLeftMaxParent;
TREE_NODE* pParent = get_parent_of_one(root, pNode); if(NULL == pNode->left && NULL == pNode->right){
if(pNode == pParent->left)
pParent->left = NULL;
else
pParent->right = NULL;
}else if(NULL != pNode->left && NULL == pNode->right){
if (pNode == pParent->left)
pParent->left = pNode->left;
else
pParent->right = pNode->left;
}else if(NULL == pNode->left && NULL != pNode->right){
if(pNode == pParent->left)
pParent->left = pNode->right;
else
pParent->right = pNode->right;
}else{
pLeftMax = get_max_node_of_one(pNode->left);
if(pLeftMax == pNode->left){
pNode->left->right = pNode->right;
if(pNode == pParent->left)
pParent->left = pNode->left;
else
pParent->right = pNode->left;
}else{
pLeftMaxParent = get_parent_of_one(root, pLeftMax);
pNode->data = pLeftMax->data;
pLeftMaxParent->right = NULL;
pNode = pLeftMax;
}
} return pNode;
} STATUS delete_node_from_tree(TREE_NODE** ppTreeNode, int data)
{
TREE_NODE* pNode;
TREE_NODE* pLeftMax;
TREE_NODE* pLeftMaxParent; if(NULL == ppTreeNode || NULL == *ppTreeNode)
return FALSE; if(NULL == (pNode = find_data_in_tree(*ppTreeNode, data)))
return FALSE; if(pNode == *ppTreeNode){
if(NULL == pNode->left && NULL == pNode->right)
*ppTreeNode = NULL;
else if(NULL != pNode->left && NULL == pNode->right)
*ppTreeNode = pNode->left;
else if(NULL == pNode->left && NULL != pNode->right)
*ppTreeNode = pNode->right;
else {
pLeftMax = get_max_node_of_one(pNode->left);
if(pNode->left == pLeftMax){
pNode->left->right = pNode->right;
*ppTreeNode = pNode->left;
}else{
pLeftMaxParent = get_parent_of_one(*ppTreeNode, pLeftMax);
pNode->data = pLeftMax->data;
pLeftMaxParent->right = NULL;
pNode = pLeftMax;
}
} goto final;
} pNode = _delete_node_from_tree(*ppTreeNode, pNode); final:
set_link_for_delete(pNode); free(pNode);
return TRUE;
}

其中,寻找最大值节点和寻找父节点的代码如下所示:

TREE_NODE* get_max_node_of_one(TREE_NODE* pNode)
{
if(NULL == pNode)
return NULL; while(pNode->right)
pNode = pNode->right; return pNode;
} TREE_NODE* get_parent_of_one(TREE_NODE* root, TREE_NODE* pNode)
{
if(NULL == root || NULL == pNode)
return NULL; while(root){
if(pNode == root->left || pNode == root->right)
return root;
else if(pNode->data < root->data)
root = root->left;
else
root = root->right;
} return NULL;
}

总结:
    (1)排序二叉树的序列化关键就是在二叉树节点添加前向指针和后继指针
    (2)排序二叉树是空间换时间的典型案例
    (3)排序二叉树是很多结构的基础,写多少遍都不为多,有机会朋友们应该多加练习
    (4)测试用例的编写是代码编写的关键,编写程序的目的就是为了消除bug,特别是低级bug

c++(排序二叉树线索化)的更多相关文章

  1. 数据结构之二叉树篇卷四 -- 二叉树线索化(With Java)

    一.线索二叉树简介 二叉树本身是一种非线性结构,然而当你对二叉树进行遍历时,你会发现遍历结果是一个线性序列.这个序列中的节点存在前驱后继关系.因此,如何将这种前驱后继信息赋予给原本的二叉树呢?这就是二 ...

  2. 记忆化搜索 codevs 2241 排序二叉树

    codevs 2241 排序二叉树 ★   输入文件:bstree.in   输出文件:bstree.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 一个边长为n的正三 ...

  3. 算法与数据结构(三) 二叉树的遍历及其线索化(Swift版)

    前面两篇博客介绍了线性表的顺序存储与链式存储以及对应的操作,并且还聊了栈与队列的相关内容.本篇博客我们就继续聊数据结构的相关东西,并且所涉及的相关Demo依然使用面向对象语言Swift来表示.本篇博客 ...

  4. 遍历二叉树 traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化

    遍历二叉树   traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化 1. 二叉树3个基本单元组成:根节点.左子树.右子树 以L.D.R ...

  5. 树和二叉树->线索二叉树

    文字描述 从二叉树的遍历可知,遍历二叉树的输出结果可看成一个线性队列,使得每个结点(除第一个和最后一个外)在这个线形队列中有且仅有一个前驱和一个后继.但是当采用二叉链表作为二叉树的存储结构时,只能得到 ...

  6. C++11 智能指针unique_ptr使用 -- 以排序二叉树为例

    用智能指针可以简化内存管理.以树为例,如果用普通指针,通常是在插入新节点时用new,在析构函数中调用delete:但有了unique_ptr类型的智能指针,就不需要在析构函数中delete了,因为当u ...

  7. 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

    树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n ...

  8. c++(排序二叉树删除)

    相比较节点的添加,平衡二叉树的删除要复杂一些.因为在删除的过程中,你要考虑到不同的情况,针对每一种不同的情况,你要有针对性的反应和调整.所以在代码编写的过程中,我们可以一边写代码,一边写测试用例.编写 ...

  9. c++(排序二叉树)

    前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点 ...

随机推荐

  1. SQL Server中varchar和nvarchar的区别

    varchar(n) 长度为 n 个字节的可变长度且非 Unicode 的字符数据.n 必须是一个介于 1 和 8,000 之间的数值.存储大小为输入数据的字节的实际长度,而不是 n 个字节.nvar ...

  2. ZooKeeper如何保证单一视图

    由于ZooKeeper的数据模型简单且全部在内存中,ZooKeeper的速度非常快.它提供了一系列保证: • 顺序一致性 • 原子性 • 单一视图 • 可靠性 • 实时性 下面将结合源码(3.4.10 ...

  3. 第三节 - centos 内核启动、救援模式、 ls 、目录结构

    Linux 第三节一.CentOS 启动: 1.内核引导: 1.win/linux 通电,2.BISO自检(CPU,内存,硬盘等 | U盘.光驱.网卡.硬盘启动 通过MBR知道内核内存硬件驱动位置并加 ...

  4. jmeter远程分布执行遇到的网卡坑(A Test is currently running,stop or ....)

    周末加班做一个项目app的性能测试,单机负载时由于公司给每个人的网络带宽上传下载流量就1M,300个用户并发就已经网络IO饱和了,虽然和相关部门协调过资源问题,但是收效甚微,因此打算先用分布部署压力机 ...

  5. for 在项目实战中用的比较多

    for循环编程语言中的语句之一,用于循环执行.for循环是开界的,它的一般形式为: for(; <条件表达式>; ) 语句: 初始化通常是一个赋值语句, 它用来给循环控制变量赋初值: 条件 ...

  6. shader 2 : use shaderToy in unity

    shadertoy 原型,https://www.shadertoy.com/view/XslGRr 先说几个概念 Shader language目前有3种主流语言:基于OpenGL的GLSL(Ope ...

  7. js构建函数,点击按钮显示div,再点击按钮或其他区域,隐藏div

    这只是一个例子,先看看效果: html代码: <nav> <span class="nav_logo"></span> <h1>云蚂 ...

  8. Java禁止浏览器有缓存的源码

    Java禁止浏览器有缓存的源码 import java.io.IOException; import javax.servlet.Filter; import javax.servlet.Filter ...

  9. 实现Windows程序的数据更新

    枚举是一组描述性的名称定义一组有限的值,不包含方法对可能的值进行约束枚举是一组指定的常数,对可能的值进行约束枚举使用时直观方便.更易于维护 pictureBox控件属性名称    说明image    ...

  10. Linux重启后raid5的名字发生变化

    Linux重启后raid5的名字发生变化 使用raid,每次重启后,都会变换设备路径 比如原来为/dev/md0 重启一次变成了/dev/md127 这个问题,可以使用修改配置文件来解决. 1.mda ...