二叉树虽然是非线性结构,但二叉树的遍历却为二叉树的结点导出了一个线性序列.对于前、中、后序遍历,除了相应序列的第一个结点和最后一个结点,二叉树的遍历序列中每个结点都有一个前驱和后继结点.
普通二叉树的遍历可能存在以下问题:
- 递归遍历有可能导致栈溢出
- 非递归版本有可能降低程序的效率
- 要想找到在某种遍历形式下某结点的前驱或后继比较难
- 树中有大量的空指针域造成浪费.比如:加入二叉树中有 n n n个结点,则有 n + 1 n+1 n+1个空指针域( n n n个结点,则有 2 n 2n 2n个指针,除了根结点外的 n − 1 n-1 n−1个结点都有一个指针指向它,所以有 n + 1 n+1 n+1个空指针域)
线索化的概念
当某结点的左指针为空时,令该指针指向按照某种方式遍历二叉树时得到该结点的前驱节点,右指针为空时,指向后继结点.
如何区分:
- 左指针指向的结点是左孩子还是前驱?
- 右指针指向的结点是右孩子还是后继?
这里添加两个线索标志位来区分这两种情况:
- leftThread(左线索):为0指向左孩子结点,为1指向前驱
- rightThread(右线索):为0指向右孩子结点,为1指向后继
结点中指向前驱结点和后继结点的指针称为线索,二叉树结点加上线索的二叉树称为线索二叉树,对二叉树以某种方式(前序、中序、后序)遍历使其变为线索二叉树的过程称为按照该方法对二叉树进行线索化.
中序线索化及中序遍历一棵树
中序线索化二叉树
关键点:在访问了当前结点并确定当前结点的左孩子域或者右孩子域为空后如何找到当前结点的前驱结点和后继结点.
思路:用prev指针保存当前结点的前一个结点、按照中序遍历的顺序线索化
- 如果当前结点的左子树域为空,则让当前结点的左孩子域指向prev所指向的结点(也就是当前结点的前驱)
- 当前结点的前一个结点的右孩子域为空,则把前一个结点的右孩子域指向当前结点
中序线索化二叉树的遍历
和非递归中序遍历类似,使得cur指针指向该树的左路结点,如果该结点右右子树则访问右子树,如果没有右子树则继续修改cur的值,使其指向当前借点的后继
前序线索化及中序遍历一棵树
前序遍历的线索化类似中序遍历的线索化,顺序是根-左-右
//前序线索化二叉树
void _PreThreading(ThreadNode* root, ThreadNode** prev){
if(root == NULL || prev == NULL){
return;
}
//处理根节点
//1.如果当前子树的根节点的左子树为空 就把left指针指向前驱
if(root->left == NULL){
root->left = *prev;
root->lflag = THREAD;
}
//如果当前子树的跟结点的前驱的右子树为空 就把前驱的right指针指向根节点
if(*prev != NULL && (*prev)->right == NULL){
(*prev)->right = root;
(*prev)->rflag = THREAD;
}
*prev = root;
if(root->lflag == CHILD){
_PreThreading(root->left, prev);
}
if(root->rflag == CHILD){
_PreThreading(root->right, prev);
}
return;
}
void PreThreading(ThreadNode* root) {
ThreadNode* prev = NULL;
_PreThreading(root, &prev);
}
//先序线索化遍历二叉树
void PreOrderByThreading(ThreadNode* root){
if(root == NULL){
return;
}
ThreadNode* cur = root;
while(cur != NULL){
while (cur->lflag == CHILD){
std::cout << cur->val << " ";
cur = cur->left;
}
std::cout << cur->val << " ";
cur = cur->right;
}
std::cout << std::endl;
}
后序线索化及中序遍历一棵树
后序线索化类似中序线索化
后序线索化遍历二叉树时,存在当前结点后继不能找到的情况
思路:按照根-右-左的顺序逆序遍历
void PostOrderByThreading(ThreadNode* root){
if(root == NULL){
return;
}
ThreadNode* cur = root;
while (cur != NULL){
while (cur->rflag == CHILD){
std::cout << cur->val << " ";
cur = cur->right;
}
std::cout << cur->val << " ";
//有前驱
while (cur->left != NULL && cur->lflag == THREAD){
cur = cur->left;
std::cout << cur->val <<" ";
}
//有子树
cur = cur->left;
}
}