数据结构 — 二叉树的线索化
来源:互联网 发布:淘宝卖家为什么改价格 编辑:程序博客网 时间:2024/05/09 04:45
二叉树的线索化
以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直 接得到结点在任一序列(先序、中序或后序序列)中的前驱和后继信息,这
种信息只有在遍历的动态过程中才能得到。为了保存这种在遍历过程中得到的信息,我们利用二叉树表中的空树域(由于结点没有左子树或右子树),
来存放结点的前驱和后继信息。
通俗一点来说,就是为了方便遍历而形成的,其实我们更多的是学习线索化的构建思想,看似简单的将空树域的的左孩子指针和右孩子指针改成指向该
种遍历方式中的该节点的前驱结点和后驱结点,其实实际实现的时候可能就会出现各种各样的问题,我随便画一个树。
这个问题我们可以通过,在结点里面添加成员,用来判断和记录该结点是否被线索化过。
enum PointerType //枚举类型{THREAD, //已线索化的LINK //连接节点,未线索化.};template<class T>struct BinaryTreeNodeThd{BinaryTreeNodeThd<T>* _letf;BinaryTreeNodeThd<T>* _right;T _data;PointerType _leftType;PointerType _rightType;BinaryTreeNodeThd(const T& x):_data(x), _letf(NULL), _right(NULL), _leftType(LINK), _rightType(LINK){}};
前序线索化
好的我们开始思考,前序线索化有一个小问题,就是当你访问到一个需要线索化的结点,你怎么知道它将要指向哪里? 它的下一个结点是谁?
我现在怎么会知道下一刻我发生什么?这个问题该如何解决呢? 来集中精力! 开始穿越剧.
我们唯一的办法就是带你去未来,让你知道你下一步应该做什么,所以这里我们不得不创建一个参数,用来传递上一个结点,我把上一个结点带到我这
里来,然后我让他需要线索化的地方指向我,而且我需要线索化的地方正好可以指向它(寻找前驱结点)。 这样就完美的解决掉这个问题.
所以我们添加一个_prev参数用来记录上一个节点的地址,让我们可以访问并操作它。(注意,这里必须_prev的引用,要不然在函数里面操作无效)
具体的代码:
//重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的. //重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的. //重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的. //MD害老子调半天void _PreOrderTheeady(Node* cur, Node* & prev){if (cur == NULL){return;}//如果是空树链,它的线索化_left指向上一个结点,并标记。if (cur->_letf == NULL){cur->_letf = prev;cur->_leftType = THREAD;}//如果是空树链,上一个结点的线索化_right指向我,并标记。if (prev&&prev->_right == NULL){prev->_right = cur;prev->_rightType = THREAD;}prev = cur;//if里的条件 防止重复访问结点if (cur->_leftType == LINK){_PreOrderTheeady(cur->_letf, prev);}//if里的条件 防止重复访问结点if (cur->_rightType == LINK){_PreOrderTheeady(cur->_right, prev);}}
这个过程画图不好描述,大家只能参照一棵树自己走一走,体会一下为什么里面会有这多的判断条件。
线索化后的结果:
中序线索化
其实意思跟前序线索化很相似,只是有的地方顺序换了一下而已。我们来看具体的代码实现:
//中序线索化void _InOrderThreading(Node* cur,Node* & prev){if (cur){_InOrderThreading(cur->_letf, prev);//如果是空树链,它的线索化_left指向上一个结点,并标记。if (cur->_letf == NULL){cur->_letf = prev;cur->_leftType = THREAD;}//如果是空树链,上一个结点的线索化_right指向我,并标记。if (prev&&prev->_right == NULL){prev->_right = cur;prev->_rightType = THREAD;}prev = cur;_InOrderThreading(cur->_right, prev);}}
如果你看懂了前序线索化,那么中序我相信你都可以直接写出来了.
我们来看看效果:
后序线索化
这里是有难度的以后补充吧...
线索化的遍历:
现在线索化的实现我们已经完成了,接下来我们遍历出结果看一看,那么我们就需要实现前序遍历的函数,和中序遍历的函数,记
好的我们从前序开始:如果你认为跟数组遍历一样,那就翻车了,因为这里的指向有点混乱如果单纯地_right的往后走很容易出错
前序遍历的实现代码:
//前序线索化遍历void prevOrder(){Node* cur = _root;while (cur){//找到最左结点while (cur->_leftType == LINK){cout << cur->_data << " ";cur = cur->_letf;}cout << cur->_data << " ";cur = cur->_right;}}
再来看看中序遍历的实现代码:
//针对中序线索化以后的遍历void inorderThd(){Node* cur = _root;while (cur){//找到最左结点,开始遍历.while (cur->_leftType == LINK){cur = cur->_letf;}cout << cur->_data << " ";//原始版本,放在这里有助于理解 /*if (cur->_rightType == LINK){cur = cur->_right;}else{while (cur->_rightType == THREAD){cur = cur->_right;cout << cur->_data << " ";}cur = cur->_right;}*///****优化后****while (cur->_rightType == THREAD){cur = cur->_right;cout << cur->_data << " ";}cur = cur->_right;}}
为什么中序遍历为什么会多几句判断条件,大家自己走一遍过程,就会理解到了呢。
具体这里最后为什么是while(cur->_rightType == THREAD)而不是if()呢?
当二叉树为这样的时候,你觉得使用if()合适吗?这里如果用if()会造成数据遍历缺失.
线索化全部代码实现与测试:
#include<iostream>#include<Windows.h>using namespace std;enum PointerType //枚举类型{THREAD, //已线索化的LINK //连接节点,未线索化.};template<class T>struct BinaryTreeNodeThd{BinaryTreeNodeThd<T>* _letf;BinaryTreeNodeThd<T>* _right;T _data;PointerType _leftType;PointerType _rightType;BinaryTreeNodeThd(const T& x):_data(x), _letf(NULL), _right(NULL), _leftType(LINK), _rightType(LINK){}};template<class T>class BinaryTreeThd{typedef BinaryTreeNodeThd<T> Node;public:BinaryTreeThd(T* a, size_t n, const T& invalid){size_t index = 0;_root = CreateTree(a, n, invalid, index);}void InOrderThreading(){Node* prev = NULL;_InOrderThreading(_root, prev);}void PreOrderTheeady(){Node* prev = NULL;_PreOrderTheeady(_root, prev);}//前序线索化遍历void prevOrder(){Node* cur = _root;while (cur){//找到最左结点while (cur->_leftType == LINK){cout << cur->_data << " ";cur = cur->_letf;}cout << cur->_data << " ";cur = cur->_right;}}/*if (cur->_leftType == THREAD || cur->_rightType == THREAD){cout << cur->_data << " ";cur = cur->_right;}*///针对中序线索化以后的遍历void inorderThd(){Node* cur = _root;while (cur){//找到最左结点,开始遍历.while (cur->_leftType == LINK){cur = cur->_letf;}cout << cur->_data << " ";//原始版本,放在这里有助于理解 /*if (cur->_rightType == LINK){cur = cur->_right;}else{while (cur->_rightType == THREAD){cur = cur->_right;cout << cur->_data << " ";}cur = cur->_right;}*///****优化后****while (cur->_rightType == THREAD){cur = cur->_right;cout << cur->_data << " ";}cur = cur->_right;}}protected://中序线索化void _InOrderThreading(Node* cur,Node* & prev){if (cur){_InOrderThreading(cur->_letf, prev);//如果是空树链,它的线索化_left指向上一个结点,并标记。if (cur->_letf == NULL){cur->_letf = prev;cur->_leftType = THREAD;}//如果是空树链,上一个结点的线索化_right指向我,并标记。if (prev&&prev->_right == NULL){prev->_right = cur;prev->_rightType = THREAD;}prev = cur;_InOrderThreading(cur->_right, prev);}}//重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的.//重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的.//重要的事情说3遍,这里的Node* & 必须加上引用,要不然上层对prev的修改就是没有意义的.//MD害老子调半天void _PreOrderTheeady(Node* cur, Node* & prev){if (cur == NULL){return;}//如果是空树链,它的线索化_left指向上一个结点,并标记。if (cur->_letf == NULL){cur->_letf = prev;cur->_leftType = THREAD;}//如果是空树链,上一个结点的线索化_right指向我,并标记。if (prev&&prev->_right == NULL){prev->_right = cur;prev->_rightType = THREAD;}prev = cur;//if里的条件 防止重复访问结点if (cur->_leftType == LINK){_PreOrderTheeady(cur->_letf, prev);}//if里的条件 防止重复访问结点if (cur->_rightType == LINK){_PreOrderTheeady(cur->_right, prev);}}//注意这里的index 要使用引用, 若是没有使用 上一层递归里面的++index 对上一层没有任何影响.//构建二叉树表Node* CreateTree(T* a, size_t n, const T& invalid, size_t& index){Node* root = NULL;if (index < n && a[index] != invalid){root = new Node(a[index]);root->_letf = CreateTree(a, n, invalid, ++index);root->_right = CreateTree(a, n, invalid, ++index);}return root;}protected:Node* _root;Node* _left;Node* _right;};void Test(){int array[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };BinaryTreeThd<int> t1(array, sizeof(array) / sizeof(array[0]), '#');/*t1.InOrderThreading();t1.inorderThd();*/t1.PreOrderTheeady();t1.prevOrder();}
6 3
- 数据结构 — 二叉树的线索化
- 数据结构之二叉树的线索化
- 数据结构--二叉树的线索化
- 【数据结构】二叉树的线索化
- 数据结构-二叉树的线索化
- 【数据结构】二叉树的线索化!!
- 数据结构——线索化二叉树
- 数据结构——线索化二叉树
- 数据结构线索化二叉树
- 数据结构::线索化二叉树
- 数据结构二叉树线索化
- 数据结构二叉树线索化
- 数据结构--树和二叉树--二叉树的线索化
- 数据结构实验 建树+线索化二叉树+线索二叉树的遍历
- 数据结构-->线索二叉树
- 数据结构--线索二叉树
- 【数据结构】线索二叉树
- 数据结构-线索二叉树
- Android ListView上下选择,定位
- SQL整理
- 不高兴的津津
- 【学习笔记】Jesse Schell's six lessons for making VR games
- MySQL
- 数据结构 — 二叉树的线索化
- Ubuntu Fluxbox GUI with x11vnc and Xvfb
- iOS 退出登录
- 661.Convert BST to Greater Tree
- Celery定时任务
- excel字段过多,导入数据库 存在截断问题的解决方法
- HTML5学习笔记 —— JavaScript开发入门
- lua脚本语言学习
- 前端框架Vue(10)——vuex 状态管理