树——二叉树的线索化

1,什么是线索化二叉树?

1,将二叉树转换为双向链表的过程(非线性 ==> 线性);

1,实际工程开发中,很大一部分情况下,二叉树一旦建立之后,就不会轻易改动,一般的用于遍历,并且这种操作一般执行很多;

2,先中后序遍历都是递归完成的,实际工程开发中,对一棵已经建立的二叉树反复执行先序遍历,效率低,所以不推荐反复的递归的遍历;

3,直接将遍历后的结果保存下来,下一次遍历直接用这个结果就可以;

4,工程开发中还有一种常见情况,就是要反复的知道某个结点在中序遍历下,前驱结点是谁、后继结点是谁,需要这三个结点一起来判断是否要执行后续的操作,这个时候也需要遍历、反复多次;

5,因此线性化二叉树就被创建出来,高效访问;

2,能够反映某种二叉树的遍历次序(结点的先后访问次序):

1,利用结点的 right 指针指向遍历(某种遍历)中的后继结点;

2,利用结点的 left 指针指向遍历(某种遍历)中的前驱结点;

2,如何对二叉树进行线索化?

1,思维过程:

3,二叉树的线索化:

1,用traversal() 函数把树结点放到队列中,然后通过connect() 函数将right 指针指向后继,left 指针指向前驱;

4,课程目标:

1,新增(在“树——二叉树的先序、中序和后续中”已经重构并存在了)功能函数 traversal(order, queue);

1,按照某种方式对二叉树进行遍历,并将遍历时结点访问的先后次序保存在 queue 中;

2,新增遍历方式 BTTraversal::LevelOrder;

1,在 traversal(order, queue)中新增层次遍历;

3,新增共有函数 BTreeNode<T>* thread(BTTraversal order);

1,用于根据遍历次序 order 执行线索化,线索化后结果为指向双向链表首元素的指针;

4,消除遍历和线索化的代码冗余(代码重构);

5,遍历功能函数代码实现:

 1    /* 遍历的功能函数,第二个参数用于保存访问时,结点的先后顺序 */
 2     void traversal(BTTraversal order, LinkQueue<BTreeNode<T>*>& queue)
 3     {
 4         switch(order)
 5         {
 6             case PreOrder:
 7                 PreOrderTraversal(root(), queue);
 8                 break;
 9             case InOrder:
10                 inOrderTraversal(root(), queue);
11                 break;
12             case PostOrder:
13                 postOrderTraversal(root(), queue);
14                 break;
15             case LevelOrder:
16                 levelOrderTraversal(root(), queue);
17                 break;
18             default:
19                 THROW_EXCEPTION(InvalidParameterException, "Parameter order is invalid ...");
20                 break;
21         }
22     }

6,层次遍历算法小结:

1,将根结点压入队列中;

2,访问队头元素指向的二叉树结点;

3,队头元素弹出,将队头元素的孩子压入队列中;

4,判断队列是否为空(非空:转 2,空:结束);

7,层次遍历算法示例:

8,层次遍历算法流程:

9,实现见代码;

 1    /* 实现层次遍历算法 */
 2     void levelOrderTraversal(BTreeNode<T>* node, LinkQueue<BTreeNode<T>*>& queue)
 3     {
 4         if( node != NULL )  // 树不为空
 5         {
 6             LinkQueue<BTreeNode<T>*> tmp;  // 创造辅助队列
 7             tmp.add(node);  // 先将根结点压入队列
 8
 9             while( tmp.length() > 0 )
10             {
11                 BTreeNode<T>* n = tmp.front();  // 拿到 tmp 头部结点
12
13                 if( n->left != NULL )
14                 {
15                     tmp.add(n->left);
16                 }
17
18                 if( n->right != NULL )
19                 {
20                     tmp.add(n->right);
21                 }
22
23                 tmp.remove();  // 移动 tmp 头部的结点
24
25                 queue.add(n);  // 保存遍历结果
26             }
27         }
28     }

10,函数接口设计:

1,BTreeNode<T>* thread(BTTraversal order)

1,根据参数 order 选择线索化的次序(先序,中序,后序,层次);

2,返回值线索化之后指向链表首结点的指针;

1,既然要不停地访问,为何不用顺序表而要用链表?

3,线索化执行结束后对应的二叉树变为空树;

1,二叉树中的结点已经被从排列为一个线性链表了,二叉树也就不存在了,所以要变为空树;

11,线索化流程:

12,队列中结点的连接算法(connect(queue)):

13,连接算法的代码实现:

 
 1    /* 将 queue 队列中结点连接成双向链表,并返回双向链表首地址 */
 2     BTreeNode<T>* connect(LinkQueue<BTreeNode<T>*>& queue)
 3     {
 4         BTreeNode<T>* ret = NULL;
 5         if( queue.length() > 0 )  // 对列不为空
 6         {
 7             ret = queue.front();  // 返回头结点
 8             BTreeNode<T>* slider = queue.front();  // 辅助指针指向对列头结点
 9             queue.remove();  // 被指向后头结点出队列
10             slider->left = NULL;  // 链表头结点指向空
11             while( queue.length() > 0 )  // 链接所有结点,就像树一样、将指针重新指定好后,就能够完成相应的容器设计了;
12             {
13                 slider->right = queue.front();
14                 queue.front()->left = slider;
15                 slider = queue.front();  // 移动 slider 指针,指向头结点;要不然就最后只有首尾两个结点了,这点很重要;
16                 queue.remove();  // 被指向后头结点出队列
17             }
18             slider->right = NULL;  // 双向链表最后一个结点指向空,不然这个双向链表就是有问题的;
19         }
20         return ret;
21     }

14,二叉树的线索化:

 1    /* 线索化,二叉树变为线性链表 */
 2     BTreeNode<T>* thread(BTTraversal order)
 3     {
 4         BTreeNode<T>* ret = NULL;  // 定义返回值
 5         LinkQueue<BTreeNode<T>*> queue;  // 先定义对列
 6         traversal(order, queue);  // 遍历,后将结果放在 queue 对列中
 7         ret = connect(queue);  // 将对列中的结点链接,并返回链表首结点
 8         this->m_root = NULL;  // 结点变为链表了,树不存在了,直接置为空树;每一个成员函数后,都要考虑成员变量的属性值,这很重要!
 9         m_queue.clear();  // 上面的树发生了变化,则清空树,否则组合函数执行的有问题
10         return ret;
11     }

15,小结:

1,线索化是将二叉树转换为双向链表的过程;

2,线索化之后结点间的先后次序符合某种遍历次序;

3,线索化操作将破坏原二叉树结点间的父子关系;

1,重新组织结点,使结点间的非线性关系变为线性关系;

4,线索化之后二叉树将不在管理结点的生命期;

1,二叉树已经不存在了,成为空树了;

2,对线索化之后的双向链表进行生命期管理,比如不需要的时候对双向链表进行释放,每个结点都释放,摧毁它;

原文地址:https://www.cnblogs.com/dishengAndziyu/p/10926140.html

时间: 2024-10-10 08:01:57

树——二叉树的线索化的相关文章

二叉树的层序遍历和二叉树的线索化

先根,后子树:先左子树,后右子树 二叉树的根节点 a 入队 a 的子树,根节点 b 和 c 分别入队 然后 b 的子树的根节点入队(为空) c 的子树的根节点入队 d 的子树的根节点入队(为空) e 的子树的根节点入队 f 的子树的根节点入队(为空) g的子树的根节点入队(为空)结束层序遍历,整个过程就是一层层的遍历,依靠一个队列来存放临时查找的结点. 二叉树线索化 问题的提出:当以二叉链表作为存储结构时,只能找到结点的左右孩子的信息,而不能直接找到结点的任一序列的前驱与后继信息,这种信息只有在

6.3线索二叉树(二叉树的线索化)

6.3线索二叉树(二叉树的线索化) 问题引入:以二叉链表作为存储结构时,只能得到结点的左.右孩子的信息,不能得到直接前驱.后继的信息. 问题解决:将二叉树线索化. 实现原理:n个结点的二叉树具有n+1个空指针域,利用这些空指针域存储结点的前驱.后继信息. 实质:线索化的实质是将二叉链表中的空指针改为指向前驱.后继的线索. (1)二叉树的存储表示 enum {link,thread};//link=0; thread=1; typedef struct bintree { char data; s

二叉树的线索化算法思想详解

二叉树的线索化,这几天以来我很难掌握,今天终于想通了,哈哈,首先我们来看看二叉树线索化之后会变成什么样子,这里我们以图中的二叉树为例,图如下: 画的太糙,各位看官讲究着看吧- -.所谓二叉树的线索化,就是当一个节点的左右指针为空时,就让它的左右指针指向该节点的前驱或者后继(一般来说左指针指向前驱,右指针指向后继).这里不论指向前驱或者后继,我们都应该线索化时,至少要明确两个节点指针的值,当前节点和当前节点的前驱/后继.这也是线索化的两种思路: 保存前驱:访问当前节点时若当前节点的左指针为空,则令

二叉树及其线索化分析

1. Where did it could work? /** *    Q: Where did it could work? * *    A: Bin-tree is generally used in deal with work about search or sort. For a  balance tree, it's depth is log2(n). That's means if we want to find a  node in the tree we need *   

二叉树的线索化

---恢复内容开始--- 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列即是对一个非线性结构进行线性化操作,使除第一个和最后一个节点外,每一个节点有且只有一个直接前驱,直接后继 二叉树作为存储结构只能找到节点的左孩子右孩子信息,而不能直接得到结点在任一序列中的前驱和后继,这种信息只有在遍历的动态过程中可以看到. 二叉树线索化可以利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息可以使用以下结点结构: enum pointertag{ Link, Thread };template

数据结构--二叉树的线索化

线索二叉树它解决了无法直接找到该结点在某种遍历序列中的前趋和后继结点的问题,出现了二叉链表找左.右孩子困难的问题,线索二叉树又分为前序线索化,中序线索化和后序线索化,分别用不同的逻辑去实现. 线索二叉树的实现思想:借用一个枚举类型tag其中包含两个状态Link(代表有数据),thread(代表下一个节点为空)在一个节点的左节点或者右节点为空的情况下,将它的left或right设为thread,则它的左或右访问的是改遍历模式下访问到的下一个节点数据,这样就完成了跳到另一颗子树的过程,减少了递归的次

c++ 二叉树的线索化(前中后序遍历)

#pragma once//.hpp函数头文件 #include<iostream> using namespace std; enum BinTreeNodeChildType { Thread, Link }; template <class T> struct BinaryTreeThrNode { public: T _data;  //数据域 BinaryTreeThrNode<T>* _leftChild;//做孩子 BinaryTreeThrNode<

第六十九课 二叉树的线索化实现

在工程中,很多时候二叉树一旦建立就不会轻易改动,这样的二叉树就用于遍历,我们讲了先序遍历.中序遍历.后续遍历三种方式,都是递归完成的,在工程中,如果对一棵二叉树反复的执行遍历,效率很低,递归的效率是比较低的. 改进的做法就是将遍历的结果保存下来,下一次遍历时直接用这个结果. 在工程中另一种需求就是,在中序遍历下,需要知道某一个节点的前驱是谁,后继是谁,需要这三个节点来判断是否执行后续的操作.这个时候又需要遍历了.每次都递归的进行遍历,效率太低了. 为了效率,我们使用线索化二叉树的方法,将二叉树转

二叉树之线索二叉树

相对于顺序存储结构而言,利用链式存储结构的二叉树已经有了很高的存储效率,单是还是有空间上未利用到的地方,比如说叶子结点的左右孩子是空的,指向左右孩子的指针就是空闲的,没有被利用到:而且,有时候给定一个结点,我们需要查找该结点的前驱结点和后继结点,如果按照中序遍历的做法去查找的话,对于一个非叶子结点,其前驱和后继结点查找可以以下算法: 1.preNode=node.left;//前去结点就是该结点的左孩子 2.subNode=search(node.right)://后继结点是该结点的右子树的最左