后序线索化二叉树
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void Tree::_postTree(Node * root, Node *&pre) { 2 if (root == NULL) { // 空值不操作 3 return; 4 } 5 else { 6 _postTree(root->lchild, pre); // 递归左树,首先是递归到左子树的最左下的结点 7 _postTree(root->rchild, pre); // 递归右树,首先是递归到左子树的最左下的第一个右结点 8 // 对当前结点进行操作,只对含有空指针的结点指针域操作 9 if (root->lchild == NULL) { // 左孩子为空,操作的是前驱,前驱是 pre 10 root->ltag = nChild; 11 root->lchild = pre; 12 } 13 // 首先要判断 pre 是否为空,后才能判断他的指针域,因为第一次的 pre在未执行下面语句 pre = root 时是NULL 值, 指针为 NULL值的没有访问其他内存空间的权限 14 if (pre != NULL && pre->rchild == NULL) { // 右孩子为空,操作的是后继, pre是前驱的话,那么root不就是后继吗? 15 pre->rtag = nChild; 16 pre->rchild = root; 17 } 18 pre = root; 19 } 20 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void Tree::Show_postTree(Node * root) { 2 cout << "后序线索化遍历(最大使用栈暂存元素个数 100):"; 3 Node * Stack[100]; // 后序遍历需要用到栈,所以该方法有局限性 4 int top = -1; 5 Node * p = root; 6 Node * pre = NULL; // 作为前驱,保留上一次访问过的指针域 7 do { 8 while (p != NULL) { // 将当前结点的左孩子全部入栈,一直走到最左边的左孩子 9 Stack[++top] = p; 10 p = p->lchild; 11 if (p == pre) { // 防止对叶子结点操作中, p = p->rchild 导致 p 不为 NULL, 在执行上面一条语句 p = p->lchild时再次访问了已访问过的结点 12 break; 13 } 14 } 15 p = Stack[top]; // 将 p 指向栈顶指针域,进行操作当前结点 16 if (p->rtag == nChild || p->rchild == pre) { // 当前结点没有右孩子 或者 它的右孩子已经被访问过 17 cout << p->data << " "; 18 top--; // 输出一个结点,就出栈一个指针域 19 pre = p; 20 p = NULL; // p 为 NULL,目的是为了使其不满足第一个while,防止再次访问已访问过的结点,转去操作下一个结点 21 } 22 else { 23 p = p->rchild; 24 } 25 } while (top != -1 && pre != NULL); 26 cout << endl; 27 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Tree::~Tree() { 2 Node * Queue[100]; 3 int rear = -1, front = -1; 4 if(root != NULL) { 5 Queue[++rear] = root; 6 } 7 Node * p = NULL; 8 while (rear != front) { 9 p = Queue[front+1]; 10 if(p->ltag == Child) { // 有左孩子就进队,没有就不管,判断有无孩子 是看它的标志 11 Queue[++rear] = p->lchild; 12 } 13 if(p->rtag == Child) { // 有右孩子入队 14 Queue[++rear] = p->rchild; 15 } 16 cout << "析构:" << p->data << endl; 17 delete p; // 删除队头所指向的那一块存储空间 18 front++; // 出队一个 front+1 19 } 20 system("pause"); 21 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 4 enum flag{ Child, nChild }; // Child 表示有孩子 nChild 即 not Child 没有孩子 5 6 struct Node { 7 char data; 8 Node * lchild; 9 Node * rchild; 10 flag ltag, rtag; // Child 表示是左或右孩子 nChild 表示前驱或后继 11 }; 12 13 class Tree { 14 public: 15 Tree(); 16 ~Tree(); 17 Node * getRoot(); 18 void Show_postTree(Node *); // 后序遍历输出 19 private: 20 Node * root; 21 Node * Create(); // 供构造函数调用初始化二叉树 22 void _postTree(Node *, Node *&); // 后序 线索化 23 }; 24 25 int main() { 26 cout << "以先序方式输入二叉树('#'代表NULL):"; 27 28 Tree T; 29 30 T.Show_postTree(T.getRoot()); 31 32 system("pause"); 33 return 0; 34 } 35 36 Tree::Tree() { 37 root = Create(); // 先创建一个默认的二叉树 38 Node * pre = NULL; 39 _postTree(root, pre); 40 } 41 42 Tree::~Tree() { 43 Node * Queue[100]; 44 int rear = -1, front = -1; 45 if(root != NULL) { 46 Queue[++rear] = root; 47 } 48 Node * p = NULL; 49 while (rear != front) { 50 p = Queue[front+1]; 51 if(p->ltag == Child) { // 有左孩子就进队,没有就不管,判断有无孩子 是看它的标志 52 Queue[++rear] = p->lchild; 53 } 54 if(p->rtag == Child) { // 有右孩子入队 55 Queue[++rear] = p->rchild; 56 } 57 cout << "析构:" << p->data << endl; 58 delete p; // 删除队头所指向的那一块存储空间 59 front++; // 出队一个 front+1 60 } 61 system("pause"); 62 } 63 64 Node * Tree::Create() { 65 Node * root; 66 char ch; 67 cin >> ch; 68 if (ch == '#') { 69 root = NULL; 70 } 71 else { 72 root = new Node(); 73 root->data = ch; 74 root->ltag = root->rtag = Child; // 默认设置左右指针域 为孩子 75 root->lchild = Create(); 76 root->rchild = Create(); 77 } 78 return root; 79 } 80 81 Node * Tree::getRoot() { 82 return root; 83 } 84 85 void Tree::_postTree(Node * root, Node *&pre) { 86 if (root == NULL) { // 空值不操作 87 return; 88 } 89 else { 90 _postTree(root->lchild, pre); // 递归左树,首先是递归到左子树的最左下的结点 91 _postTree(root->rchild, pre); // 递归右树,首先是递归到左子树的最左下的第一个右结点 92 // 对当前结点进行操作,只对含有空指针的结点指针域操作 93 if (root->lchild == NULL) { // 左孩子为空,操作的是前驱,前驱是 pre 94 root->ltag = nChild; 95 root->lchild = pre; 96 } 97 // 首先要判断 pre 是否为空,后才能判断他的指针域,因为第一次的 pre在未执行下面语句 pre = root 时是NULL 值, 指针为 NULL值的没有访问其他内存空间的权限 98 if (pre != NULL && pre->rchild == NULL) { // 右孩子为空,操作的是后继, pre是前驱的话,那么root不就是后继吗? 99 pre->rtag = nChild; 100 pre->rchild = root; 101 } 102 pre = root; 103 } 104 } 105 106 void Tree::Show_postTree(Node * root) { 107 cout << "后序线索化遍历(最大使用栈暂存元素个数 100):"; 108 Node * Stack[100]; 109 int top = -1; 110 Node * p = root; 111 Node * pre = NULL; // 作为前驱,保留上一次访问过的指针域 112 do { 113 while (p != NULL) { // 将当前结点的左孩子全部入栈,一直走到最左边的左孩子 114 Stack[++top] = p; 115 p = p->lchild; 116 if (p == pre) { // 防止对叶子结点操作中, p = p->rchild 导致 p 不为 NULL, 在执行上面一条语句 p = p->lchild时再次访问了已访问过的结点 117 break; 118 } 119 } 120 p = Stack[top]; // 将 p 指向栈顶指针域,进行操作当前结点 121 if (p->rtag == nChild || p->rchild == pre) { // 当前结点没有右孩子 或者 它的右孩子已经被访问过 122 cout << p->data << " "; 123 top--; // 输出一个结点,就出栈一个指针域 124 pre = p; 125 p = NULL; // p 为 NULL,目的是为了使其不满足第一个while,防止再次访问已访问过的结点,转去操作下一个结点 126 } 127 else { 128 p = p->rchild; 129 } 130 } while (top != -1 && pre != NULL); 131 cout << endl; 132 }