以前咱们研究二叉树,今天咱们继续研究二叉树的优化--线索二叉树。markdown
在二叉树中,结点的度为0~2的范围。当度为0或1时,结点中为指向孩子的空间是没有用的,如图: 学习
浪费的空间利用起来,把没用的左右孩子的区域用来指向前驱或者后继结点。这样树在使用起来的时候,减小遍历的次数,这样会更有效率。如图: 优化
经过上面的图片,很容易理解线索二叉树的相关逻辑。可是咱们会遇到一个问题:如何区分孩子区域指向的是孩子结点仍是前驱或后继结点呢? spa
经过对结点增长标志位来区分是孩子结点仍是前驱后继结点。 指针
线索化的二叉树,其实和咱们以前学习过的双向链表很相似;因此咱们能够给二叉树增长一个头结点,这样在遍历的时更便捷: code
#include <stdio.h> #include "string.h" #include "stdlib.h" #include "math.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 typedef int Status; typedef char CElemType; CElemType Nil = '#'; int indexs = 1; typedef char String[24]; String str; Status strAssign(String T, char *chars) { if (strlen(chars) > MAXSIZE) { return ERROR; } else { T[0]=strlen(chars);//下标0的位置存储字符串长度 for (int i = 1; i <= T[0]; i++) { T[i] = *(chars + i - 1); } return OK; } } /** Link == 0,标示指向左右孩子指针 Thread==1,标示指向前驱或后继的线索 */ typedef enum {Link, Thread} PointerTag; /** 线索二叉树结构体 */ typedef struct BiThrNode{ CElemType data; struct BiThrNode *lchild, *rchild; PointerTag lTag, rTag; }BiThrNode, *BiThrTree; /** 打印 */ Status visit(CElemType e) { printf("%c ", e); return OK; } /** 构造二叉树 */ Status createBiThrTree(BiThrTree *T) { CElemType h; h = str[indexs++]; if (h == Nil) { *T = NULL; } else { *T = (BiThrTree)malloc(sizeof(BiThrNode)); if (!*T) { exit(OVERFLOW); } (*T)->data = h; createBiThrTree(&(*T)->lchild); if ((*T)->lchild) {//左孩子存在,标记为孩子指针 (*T)->lTag = Link; } createBiThrTree(&(*T)->rchild); if ((*T)->rchild) {//右孩子存在,标记为孩子指针 (*T)->rTag = Link; } } return OK; } BiThrTree pre;//全局变量,指向前一个结点 void inThreading(BiThrTree p) { if (p) { //递归左子树 inThreading(p->lchild); if (!p->lchild) {//左孩子不存在,设置作孩子前驱线索 p->lTag = Thread; p->lchild = pre; } else { p->lTag = Link; } if (!pre->rchild) {//前驱先说右孩子不存在,设置前驱结点的右孩子线索为当前结点 pre->rTag = Thread; pre->rchild = p; } else { pre->rTag = Link; } pre = p; //递归右子树 inThreading(p->rchild); } } /** 中序遍历二叉树T,并线索化,建立头结点 */ Status inOrderThreading(BiThrTree T, BiThrTree *Thrt) { //头结点 *Thrt = (BiThrTree)malloc(sizeof(BiThrNode)); if (!*Thrt) { exit(OVERFLOW); } //头结点左孩子标记为孩子指针 (*Thrt)->lTag = Link; //头结点右孩子标记为线索指针 (*Thrt)->rTag = Thread; //头结点右孩子指向本身 (*Thrt)->rchild = (*Thrt); if (!T) { (*Thrt)->lchild = *Thrt; } else { //头结点孩子指针指向根结点 (*Thrt)->lchild = T; //前驱结点指向头结点 pre = (*Thrt); //中序遍历进行线索化 inThreading(T); //最后一个结点线索化 pre->rchild = *Thrt; pre->rTag = Thread; (*Thrt)->rchild = pre; } return OK; } /** 中序遍历二叉线索树 */ Status inOrderTraverse(BiThrTree T) { BiThrTree p; p=T->lchild;//p指向根结点 while (p!=T) {//空树或者遍历指向头结点时结束 while (p->lTag == Link) {//找到第一个左孩子结点 p = p->lchild; } if (!visit(p->data)) {//访问左子树为空的结点 return ERROR; } //遍历条件右孩子指针是线索指针而且右孩子不是指向头结点 while (p->rTag == Thread && p->rchild != T) { p = p->rchild; visit(p->data); } p = p->rchild; } return OK; } 复制代码
int main(int argc, const char * argv[]) { // insert code here... printf("Hello, 线索二叉树!\n"); BiThrTree H,T; strAssign(str,"ABDH##I##EJ###CF##G##"); createBiThrTree(&T); /* 按前序产生二叉树 */ inOrderThreading(T,&H); /* 中序遍历,并中序线索化二叉树 */ inOrderTraverse(H); printf("\n"); return 0; } 复制代码