1、線索化二叉樹的定義和初始化
#include<stdio.h> #include<malloc.h> #include<assert.h> #define ElemType char typedef struct BinTreeNode{ ElemType data; struct BinTreeNode* leftChild; struct BinTreeNode* rightChild; int lTage;//左標記[0:左孩子節點 1:前驅節點] int rTage;//右標記[0:右孩子節點 1:後繼節點] }BinTreeNode; typedef struct BinTree{ BinTreeNode* root; ElemType refvalue;//停止標誌 }BinTree; //初始化 void initBinTree(BinTree* bt,ElemType ref){ bt->root = NULL; bt->refvalue = ref; }
2、二叉樹的建立和線索化
//建立節點 BinTreeNode* malloc_node(ElemType data){ BinTreeNode* t = (BinTreeNode*) malloc(sizeof(BinTreeNode)); assert(t != NULL); t->data = data; t->leftChild = NULL; t->rightChild = NULL; t->lTage = t->rTage = 0; return t; } //建立二叉樹 void createBinTree(BinTree* bt,BinTreeNode** node,char** str){ if(**str == bt->refvalue){ (*node) = NULL; ++(*str); } else { (*node) = malloc_node(**str); ++(*str); createBinTree(bt,&(*node)->leftChild,str); createBinTree(bt,&(*node)->rightChild,str); } } //二叉樹中序線索化 void BinTreeNodeInThread(BinTreeNode** t,BinTreeNode** pre){ if(*t == NULL) return; else { //遍歷當前節點的所有左子樹 BinTreeNodeInThread(&(*t)->leftChild,pre); //執行到此處說明該節點 的左子樹已全部遍歷完 //處理該節點 將左節點進行線索化 if((*t)->leftChild == NULL){ (*t)->lTage = 1; (*t)->leftChild = *pre; } //處理該節點的前驅節點,將該節點的前驅節點線索化[前驅節點的後繼指向t] if((*pre) != NULL && (*pre)->rightChild == NULL){ (*pre)->rTage = 1; (*pre)->rightChild = *t; } //更新前驅節點 *pre = *t; //遍歷當前節點的所有右子樹 BinTreeNodeInThread(&(*t)->rightChild,pre); } } void BinTreeInThread(BinTree* bt){ //記錄前驅節點 BinTreeNode* pre = NULL; BinTreeNodeInThread(&bt->root,&pre); //處理最後一個節點 if(pre != NULL){ pre->rightChild = NULL; pre->rTage = 1; } }
3、線索化二叉樹的其他方法
//求中序遍歷第一個節點 BinTreeNode* First(BinTreeNode* node){ BinTreeNode* p = node; //遍歷左孩子,當遍歷到左標記為1說明是第一個節點 //由於已經線索化了,所有節點的左右孩子都不為NULL while(p != NULL && p->lTage == 0) p = p->leftChild; return p; } //求中序遍歷最後一個節點 BinTreeNode* Last(BinTreeNode* node){ BinTreeNode* p = node; while(p != NULL && p->rTage == 0){ p = p->rightChild; } return p; } //求cur節點的後繼 BinTreeNode* Next(BinTreeNode* root,BinTreeNode* cur){ if(root == NULL || cur == NULL) return NULL; if(cur->rTage == 1) return cur->rightChild; //由於是中序遍歷,說明cur左子樹的節點都遍歷完了, //所以cur的後繼節點為它的右子樹遍歷的第一個節點 return First(cur->rightChild); } //求cur節點的前驅 BinTreeNode* Prio(BinTreeNode* root,BinTreeNode* cur){ if(root == NULL || cur == NULL) return NULL; if(cur->lTage == 1) return cur->leftChild; //由於是中序遍歷,cur的前驅為它的左子樹遍歷的最後一個節點 return Last(cur->leftChild); } //中序遍歷[由於二叉樹已經線索化,不能再用之前的遍歷方法, //線索化將二叉樹 由非線性變為線性關係] void InOrder(BinTreeNode* root){ BinTreeNode* p = root; for(p = First(p);p != NULL;p = Next(root,p)){ printf("%c ",p->data); } printf("\n"); } BinTreeNode* search(BinTreeNode* root,ElemType key){ if(root == NULL) return NULL; if(root->data == key) return root; BinTreeNode* p = root; for(p = First(p);p != NULL;p = Next(root,p)){ if(p->data == key) return p; } return NULL; } BinTreeNode* Parent(BinTreeNode* root, BinTreeNode* cur){ if (root == NULL || cur == NULL || root == cur) { return NULL; } BinTreeNode* p; //cur存線上索 注意:if中一定要判斷p是否為空 if(cur->lTage == 1){ p = cur->leftChild; //由於是中序遍歷,cur的左孩子為前驅節點,既然是前驅說明cur一定在指向的右子樹這邊 if(p != NULL && p->rightChild == cur) return p; } if(cur->rTage == 1){ p = cur->rightChild; if(p != NULL && p->leftChild == cur) return p; } //cur不存線上索 ,說明cur存在左子樹,那麼左子樹在cur前面 //找到cur左子樹要顯示的第一個節點,該節點一定沒有左子樹 //故該節點的左孩子節點會指向它的前驅,這個前驅有可能就是cur的父節點 p = First(cur->leftChild); p = p->leftChild; if(p != NULL && p->rightChild == cur) return p; //如果上訴都沒找到,那就找cur節點的右孩子最後中序遍歷的最後一個節點 //這個節點的後繼就是cur的父節點,因為這個節點在cur右邊,說明cur已經訪問了 //cur的中序遍歷最後一個右節點也訪問,根據左根右說明下一個就是cur的父節點 p = Last(cur->leftChild); if (p != NULL && p->rightChild == cur) { return p; } return NULL; } int main(){ BinTree myTree; initBinTree(&myTree,'#'); char* str = "ABC##DE##F##G#H##"; createBinTree(&myTree,&myTree.root,&str); BinTreeInThread(&myTree); // BinTreeNode* node = Last(myTree.root); // printf("%c",node->data); InOrder(myTree.root); BinTreeNode* p = search(myTree.root,'C'); // BinTreeNode* next = Next(myTree.root,p); BinTreeNode* parent = Parent(myTree.root,p); printf("%c",parent->data); return 0; }