線索二叉樹利用二叉樹空余的指針域,來實現二叉樹的鏈式化。然后,就可以通過前驅,后繼像雙向鏈表一樣根據某種遍歷次序對樹的結點進行訪問。
數據結構:
1 struct node{ 2 int data; 3 struct node* left,*right; 4 int ltag,rtag; //=0時,表明指向子結點;=1時,表示指向前驅/后繼 5 }
線索二叉樹的操作:
建立線索二叉樹:
- 不同的遍歷順序,會得到不同的線索二叉樹。
- 一般使第線索鏈表的頭和尾指向NULL(也可以加入一個頭指針)
不同的遍歷順序形成的二叉樹還是具有不同的特點,因此,對每一個建立完整的建樹和操作過程來查看性質。
中序遍歷建立二叉樹:
- 在最初中序遍歷的基礎上(左子樹,根結點,右子樹),從處理一個結點變為處理一對結點。
- 建樹的時候根據left/right==nullptr; 當建立完成后根據ltag/lright進行判斷;
1 #include<bits\stdc++.h> 2 using namespace std; 3 4 struct node { 5 int data; 6 int ltag=0, rtag=0; //=0,表示指向子樹. 7 node* left=nullptr, * right=nullptr; 8 }; 9 10 typedef struct node Node; 11 12 Node* CreateTree() { 13 Node* root=new Node; 14 root->data = 1; 15 root->left = new Node; 16 root->left->data = 2; 17 root->right = new Node; 18 root->right->data = 3; 19 root->left->left = new Node; 20 root->left->left->data = 4; 21 root->left->right = new Node; 22 root->left->right->data = 5; 23 root->right->left = new Node; 24 root->right->left->data = 6; 25 root->right->right = new Node; 26 root->right->right->data = 7; 27 return root; 28 } 29 30 // 建立cur和pre兩個結點的線索關系,注意:pre的類型是Node* &(如果是Node*是不能正確形成right線索的) 31 void CreateClueInOrder(Node* cur, Node* &pre) { 32 if (cur != nullptr) { 33 // 遞歸思想:左子樹,根結點,右子樹 34 // 左子樹 35 CreateClueInOrder(cur->left, pre); 36 // 根結點,在建立線索的時候根據left/right==nullptr進行判斷; 37 if (cur->left == nullptr) { 38 cur->left = pre; 39 cur->ltag = 1; 40 } 41 if (pre != nullptr && pre->right == nullptr) { 42 pre->right = cur; 43 pre->rtag = 1; 44 } 45 pre = cur; 46 // 右子樹 47 CreateClueInOrder(cur->right, pre); 48 } 49 } 50 51 // 以中序遍歷方式建立線索二叉樹 52 void CreateClueTreeInOrder(Node* root) { 53 if (root != nullptr) { 54 Node* pre = nullptr; 55 CreateClueInOrder(root, pre); 56 pre->ltag = 1; // pre此時為中序遍歷最后一個結點,設置ltag 57 } 58 } 59 60 int main() { 61 Node* root = CreateTree(); 62 CreateClueTreeInOrder(root); 63 }
中序線索二叉樹的線索特性:
- 如果一個結點有左子樹,那么其前驅就是左子樹的最右結點。 如果沒有左子樹,通過線索指明前驅。
- 如果一個結點有右子樹,那么其后繼就是右子樹的最左結點。 如果沒有右子樹,通過線索指明后繼。
- 因此對於二叉樹的任意一個頂點都無需通過棧來實現尋找前驅和后繼。 (可以直接尋找到,只有中序才有該種性質,先序和后序是沒有的,可以往下觀看)

1 #include<bits\stdc++.h> 2 using namespace std; 3 4 struct node { 5 int data; 6 int ltag=0, rtag=0; //=0,表示指向子樹. 7 node* left=nullptr, * right=nullptr; 8 }; 9 10 typedef struct node Node; 11 12 Node* CreateTree() { 13 Node* root=new Node; 14 root->data = 1; 15 root->left = new Node; 16 root->left->data = 2; 17 root->right = new Node; 18 root->right->data = 3; 19 root->left->left = new Node; 20 root->left->left->data = 4; 21 root->left->right = new Node; 22 root->left->right->data = 5; 23 root->right->left = new Node; 24 root->right->left->data = 6; 25 root->right->right = new Node; 26 root->right->right->data = 7; 27 return root; 28 } 29 30 // 建立cur和pre兩個結點的線索關系,注意:pre的類型是Node* &(如果是Node*是不能正確形成right線索的) 31 void CreateClueInOrder(Node* cur, Node* &pre) { 32 if (cur != nullptr) { 33 // 遞歸思想:左子樹,根結點,右子樹 34 // 左子樹 35 CreateClueInOrder(cur->left, pre); 36 // 根結點,在建立線索的時候根據left/right==nullptr進行判斷; 37 if (cur->left == nullptr) { 38 cur->left = pre; 39 cur->ltag = 1; 40 } 41 if (pre != nullptr && pre->right == nullptr) { 42 pre->right = cur; 43 pre->rtag = 1; 44 } 45 pre = cur; 46 // 右子樹 47 CreateClueInOrder(cur->right, pre); 48 } 49 } 50 51 // 以中序遍歷方式建立線索二叉樹 52 void CreateClueTreeInOrder(Node* root) { 53 if (root != nullptr) { 54 Node* pre = nullptr; 55 CreateClueInOrder(root, pre); 56 pre->ltag = 1; // pre此時為中序遍歷最后一個結點,設置ltag 57 } 58 } 59 60 // 獲得線索二叉樹的第一個結點 61 Node* GetFirstNodeInOrder(Node* root) { 62 // 注意:這里需要使用root->ltag==0而不能使用root->left!=nullptr 63 // 因為除了首結點,原來的葉子節點的前驅變為了線索;而且這個函數是復用在查找中途; 64 while (root!=nullptr && root->ltag==0) { 65 root = root->left; 66 } 67 return root; 68 } 69 70 // 根據當前結點獲得下一個結點 71 Node* GetNextNodeInOrder(Node* p) { 72 // 有右子樹,獲得右子樹的最左結點; 73 if (p->rtag == 0) { 74 p=GetFirstNodeInOrder(p->right); 75 } 76 else { 77 p = p->right; 78 } 79 return p; 80 } 81 82 void TraverseInOrder(Node* root) { 83 root = GetFirstNodeInOrder(root); // 獲得起始結點 84 while (root != nullptr) { 85 cout << root->data << " "; 86 root = GetNextNodeInOrder(root); 87 } 88 } 89 90 int main() { 91 Node* root = CreateTree(); 92 CreateClueTreeInOrder(root); 93 TraverseInOrder(root); 94 }
先序遍歷建立二叉樹:
- 在建立的時候,可能會遇到之前建立的線索,因此需要進行判斷,防止進入死循環。
1 // 先序遍歷建立線索二叉樹 2 void CreateCluePreOrder(Node* cur, Node* &pre) { 3 if (cur != nullptr) { 4 // 根結點,在建立線索的時候根據left/right==nullptr進行判斷; 5 if (cur->left == nullptr) { 6 cur->left = pre; 7 cur->ltag = 1; 8 } 9 if (pre != nullptr && pre->right == nullptr) { 10 pre->right = cur; 11 pre->rtag = 1; 12 } 13 pre = cur; 14 // 這里和中序有點區別! 15 if (pre->ltag == 0) { 16 CreateCluePreOrder(cur->left, pre); 17 } 18 if (pre->rtag == 0) { 19 CreateCluePreOrder(cur->right, pre); 20 } 21 } 22 } 23 24 // 建立先序線索二叉樹 25 void CreateClueTreePreOrder(Node* root) { 26 Node* pre = nullptr; 27 if (root != nullptr) { 28 CreateCluePreOrder(root, pre); 29 pre->rtag = 1; 30 } 31 }
先序線索二叉樹的特性:
- 對於前驅而言:如果左子樹為空,那么可以通過線索指出; 如果左子樹不為空,那么必須通過先序遍歷的遞歸序列指出。(必須依靠棧/遞歸來實現前驅的尋找)
- 對於后序而言:如果左子樹為空,那么通過線索指出; 如果不為空,就是左子樹的根結點。
- 因為對於先序遍歷的左孩子和右孩子指向的都是后繼信息。因此就無法尋找前驅信息了。 (先序遍歷線索二叉樹無法直接尋找某些結點的前驅,但還是能夠直接實現遍歷)
- 同樣,對於后序遍歷,左孩子和右孩子指向的都是前驅信息,因此無法尋找后繼信息。 (后序遍歷線索二叉樹無法直接尋找某些結點的后繼,無法直接實現遍歷)