后序线索二叉树的创建算法思想:后续遍历树中每一个结点,访问每一对前驱和后继,如果当前结点左孩子为空,则将左孩子指针指向前驱结点,将左指针标记域设置为真;如果前驱结点右孩子指针为空,则将右孩子指针域指向后继结点,将右指针标记域设置为真。
后序遍历后续线索二叉树算法思想:不同于先序与中序遍历寻找当前结点的后继结点,后序遍历是寻找当前结点的前驱结点,并压入栈中,直到遍历完树中所有结点,最后将栈中元素弹出即实现后序遍历二叉树。
寻找当前结点的前驱结点算法思想:如果当前结点左指针标记域为真,即当前结点不存在左孩子,则左指针指向当前孩子的前驱结点;如果当前结点左指针标记域为假,查看当前结点右指针域,如果当前结点右指针域为假,则当前结点存在右孩子,根据后续遍历规则,当前结点的前驱结点为右指针指向的结点;如果如果当前结点左指针标记域为假,且结点右指针域为真,则当前结点不存在右孩子,只存在左孩子,则前驱结点为左孩子指向的结点。
(1)fun.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//树相关数据结构
typedef char ElemType;
typedef struct BSTNode {
ElemType data;
struct BSTNode* lchild, * rchild;
int ltag, rtag;
}BSTNode, * BSTree;
//先序创建树
void creatTree(BSTree& T);
//访问结点
void visit1(BSTNode* pnode);
//后序遍历树,检验建树与遍历是否成功
void postOrder(BSTree T);
//初始化标记位
void initial(BSTree& T);
//后续线索二叉树
//算法思想:后续遍历树中每一个结点,并进行线索化
void postThrTree(BSTNode* pcurnode);
void creatPostBSTree(BSTree T);
//得到当前结点前驱结点
BSTNode* getPreNode(BSTNode* pcurnode);
//通过线索后续遍历线索二叉树;
void postOrderBSTree(BSTree T);
//链栈相关数据结构
typedef char SElemType;
typedef struct LinkNode {
SElemType data;
struct LinkNode* next;
}LinkNode, * LinkStack;
//初始化栈
void initial(LinkStack& S);
//判栈空
int isEmpty(LinkStack S);
// 入栈(头部插入)
void push(LinkStack & S, SElemType x);
//出栈(头部删除)
void pop(LinkStack& S, SElemType& x);
(2)Stack.app
#include"func.h"
//初始化栈
void initial(LinkStack& S) {
S = (LinkNode*)malloc(sizeof(LinkNode));
S->next = NULL;
}
//判栈空
int isEmpty(LinkStack S) {
if (S->next == NULL) {
return 1;
}
else {
return 0;
}
}
//入栈(头部插入)
void push(LinkStack &S, SElemType x) {
LinkNode* pnode = (LinkNode*)malloc(sizeof(LinkNode));
pnode->data = x;
pnode->next = S->next;
S->next = pnode;
}
//出栈(头部删除)
void pop(LinkStack& S, SElemType& x) {
LinkNode* pnode = S->next;
x = pnode->data;
S->next = pnode->next;
}
(3)BSTree.cpp
#include"func.h";
//先序创建树
void creatTree(BSTree& T) {
ElemType ch;
scanf("%c", &ch);
if (ch == '#') {
T = NULL;
}
else {
T = (BSTNode*)malloc(sizeof(BSTNode));
T->data = ch;
creatTree(T->lchild);
creatTree(T->rchild);
}
}
//访问结点
void visit1(BSTNode* pnode) {
printf("%c", pnode->data);
}
//后序遍历树,检验建树与遍历是否成功
void postOrder(BSTree T) {
if (T != NULL) {
postOrder(T->lchild);
postOrder(T->rchild);
visit1(T);
}
}
//初始化标记位
void initial(BSTree& T) {
if (T != NULL) {
T->ltag = 0;
T->rtag = 0;
initial(T->lchild);
initial(T->rchild);
}
}
//后续线索二叉树
//算法思想:后续遍历树中每一个结点,并进行线索化
BSTNode* prenode = NULL;
void visit2(BSTNode* pcurnode) {
if (pcurnode->lchild == NULL) {
pcurnode->lchild = prenode;
pcurnode->ltag = 1;
}
if (prenode != NULL&& prenode->rchild == NULL) {//注意需要先判空,再判右孩子为空,否则会报错
prenode->rchild = pcurnode;
prenode->rtag = 1;
}
prenode = pcurnode;//更新前驱结点
}
void creatBSTree(BSTree T) {
if (T != NULL) {
creatBSTree(T->lchild);
creatBSTree(T->rchild);
visit2(T);
}
}
//后序建树主过程,注意最后一个结点的处理
void creatPostBSTree(BSTree T) {
prenode = NULL;
if (T != NULL) {
creatBSTree(T);
if (prenode->rchild == NULL) {//处理最后一个结点
prenode->rtag = 1;
}
}
}
//得到当前结点前驱结点
BSTNode* getPreNode(BSTNode* pcurnode) {
if (pcurnode->ltag == 1) {//如果当前结点左标记为1,则左孩子为前驱结点
return pcurnode->lchild;
}//左右根
else {//否则,当前结点左标记为0,当前结点存在左孩子
if (pcurnode->rtag == 0) {//如果当前结点右标记为真,根据后续遍历规则,则右孩子为前驱结点
return pcurnode->rchild;
}
else {//否则,当前结点没有右孩子,根据后续遍历规则,当前结点不存在右孩子,存在左孩子,则左孩子为前驱
return pcurnode->lchild;
}
}
}
LinkStack S = NULL;//利用栈存储遍历序列
//通过线索后续遍历线索二叉树
void postOrderBSTree(BSTree T) {
initial(S);//初始化栈
for (BSTNode* pcurnode = T; pcurnode != NULL; pcurnode = getPreNode(pcurnode)) {
push(S, pcurnode->data);//将当前结点数值插入栈中
}
}
//AB#D##CE###
int main() {
BSTree Thr = NULL;
creatTree(Thr);
postOrder(Thr);
printf("\n");
initial(Thr);
creatPostBSTree(Thr);//后续线索化二叉树
postOrderBSTree(Thr);//遍历后续线索二叉树
while (!isEmpty(S)) {
ElemType x;
pop(S, x);
printf("%c", x);
}
printf("\n");
}