以前咱們研究二叉樹,今天咱們繼續研究二叉樹的優化--線索二叉樹。markdown
在二叉樹中,結點的度爲0~2的範圍。當度爲0或1時,結點中爲指向孩子的空間是沒有用的,如圖: 學習
圖中結點F、G、H、I、J的度爲0,也就是沒有左右孩子。結點E的度爲1,沒有右孩子。會有必定的空間浪費。線索二叉樹就是利用這些浪費的空間,進而提升了對二叉樹操做時的效率。浪費的空間利用起來,把沒用的左右孩子的區域用來指向前驅或者後繼結點。這樣樹在使用起來的時候,減小遍歷的次數,這樣會更有效率。如圖: 優化
圖中使用的中序遍歷的方式,中序遍歷結果爲:HDIBJEAFCG經過上面的圖片,很容易理解線索二叉樹的相關邏輯。可是咱們會遇到一個問題:如何區分孩子區域指向的是孩子結點仍是前驅或後繼結點呢? spa
例如:圖中咱們知道結點E的左孩子是J,而J結點的右孩子區域用於指向後繼E結點。經過對結點增長標誌位來區分是孩子結點仍是前驅後繼結點。 指針
結點中0表明的是孩子結點,1代買是前驅後繼結點。左邊和右邊分別表明左孩子區域標示和右孩子區域標示。線索化的二叉樹,其實和咱們以前學習過的雙向鏈表很相似;因此咱們能夠給二叉樹增長一個頭結點,這樣在遍歷的時更便捷: 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; } 複製代碼