資料結構-線索二叉樹(中序線索二叉樹及遍歷)
1.二叉樹線索化
二叉樹的遍歷是按照一定的規則把二叉樹中的節點按照一定的次序排列成線性序列進行訪問的,實質上就是對一個非線性結構進行線索化操作,使得每個節點(除第一個和最後一個外)都有前驅和後繼節點,有時為了運算方便需要記錄這些前驅和後繼節點,稱為二叉樹線索化,而對於不同的遍歷規則,又分為先序線索二叉樹,中序線索二叉樹,後序線索二叉樹。
2.線索二叉樹的定義
(1)思路:
一顆具有n個節點的二叉樹,有n-1條指向左孩子或右孩子的分支,對於二叉連結串列來說,2n個指標只用了n-1個,所以可以利用另外n+1個指標作為指向前驅節點和後繼節點的指標。
(2)前驅節點和後繼節點
有些書籍對這兩個概念並沒有簡單明瞭的解釋~,其實就是對於特定遍歷方式來說,一個節點的前後節點
①前驅節點:
指定遍歷方式得出的節點訪問順序中,某一節點的前一個節點。
②後繼節點
指定遍歷方式得出的節點訪問順序中,某一節點的後一個節點。
(3)線索二叉樹的節點表示(結構體)-線索連結串列(Thread Linked List)
#define Link 0 //表示該節點有非空孩子節點 #define Thread 1 //表示該節點有後續節點(對於右子樹來說) template<class T> struct BT_Thread_Node { T Data; BT_Thread_Node* Left_Child; BT_Thread_Node* Right_Child; int Ltag; int Rtag; };
Ltag=0(Link)-表示Left_Child的指標指向左孩子節點;Ltag=1(Thread)-表示Left_Child的指標指向前驅節點
Rtag=0(Link)-表示Right_Child的指標指向右孩子節點;Rtag=1(Thread)-表示Right_Child的指標指向後繼節點
(4)線索化規則(重點)
書上也是奇奇怪怪的,其實很簡單,就按照某一遍歷規則,記錄當前節點(Cur),上一次訪問的節點(Pre)
①如果當前節點Cur->Left_Child=NULL(左孩子節點空),則Cur->Left_Child=Pre;Cur->Ltag=1(
②如果前一節點Pre->Right_Child=NULL(前節點的右孩子節點空),則Pre->Right_Child=Cur;Pre->Rtag=1(前節點的右指標域指向後繼節點(也就是當前節點),修改Rtag為線索模式)
3.中序線索二叉樹
(1)線索化中序二叉樹
(2)中序線索化實現
template<class T>
void Thread_Binary_tree<T>::InOrder_Thread_Op(BT_Thread_Node<T>* &Tree)
{
if (Tree == NULL) //空返回上一節點
return;
InOrder_Thread_Op(Tree->Left_Child); //左
if (Tree->Left_Child == NULL) //根
{
Tree->Ltag = Thread;
Tree->Left_Child = Pre_Node;
}
if (Pre_Node != NULL && Pre_Node->Right_Child == NULL)
{
Pre_Node->Rtag = Thread;
Pre_Node->Right_Child = Tree;
}
Pre_Node = Tree;
InOrder_Thread_Op(Tree->Right_Child); //右
}
注:Pre_Node是類內資料成員,初始化為NULL
(3)線索化遍歷
①思路:
對於中序遍歷來說,先查詢線索連結串列的第一個節點,也就是最左方向上的最後一個節點,然後如果有右線索先尋找後繼節點,查詢到斷線索(有右節點啦)就往下找一個右節點,繼續這樣摸下去,其實說到底就是有線索先按線索找(注意線索上的節點是需要訪問的),等到線索斷了就往下找右孩子節點。
②實現程式碼
template<class T>
void Thread_Binary_tree<T>::_InOrder_Op(BT_Thread_Node<T>* &Tree)
{
if (Tree == NULL)
return;
BT_Thread_Node<T>* Cur_Node = Tree;
while (Cur_Node) //當前節點不能為空
{
while (Cur_Node->Ltag == Link) //節點有左樹時,尋找最左端的樹
{
Cur_Node = Cur_Node->Left_Child;
}
cout << Cur_Node->Data << " ";
while (Cur_Node&&Cur_Node->Rtag == Thread) //節點非空並且右樹是線索樹時查詢最前的一個線索樹節點
{
Cur_Node = Cur_Node->Right_Child;
cout << Cur_Node->Data << " ";
}
Cur_Node = Cur_Node->Right_Child; //右樹不是線索樹,查詢該節點的右孩子節點
}
cout << endl;
}
(4)二叉樹的析構(利用線索)
①思路:
當把樹線索化了,其實就是連結串列,而刪除呢,我覺得按連結串列刪就ok,但是後來發現腦子不好用,就想著按遍歷的順序,棧存節點,然後把棧清了就ok,所以再寫了一個程式,寫了才發現其實!可以直接在遍歷的時候順便入棧了節點,好吧這次先這樣,等後面出先序和後序我再改吧~
②程式碼
BT_Thread_Node<T>* BT_Node_Stack[MAXSIZE];//類私有,MAXISIZE=100
template<class T>
int Thread_Binary_tree<T>::Distory_Thread_BTree(BT_Thread_Node<T>* &Tree)
{
if(Tree==NULL)
return 0;
int Top = -1;
BT_Thread_Node<T>* Cur_Node = Tree;
while (Cur_Node)
{
while (Cur_Node->Ltag == Link)
{
Cur_Node = Cur_Node->Left_Child;
}
BT_Node_Stack[++Top] = Cur_Node;
while (Cur_Node&&Cur_Node->Rtag == Thread)
{
Cur_Node = Cur_Node->Right_Child;
BT_Node_Stack[++Top] = Cur_Node;
}
Cur_Node = Cur_Node->Right_Child;
}
for (Top; Top != -1; Top--)
{
cout << BT_Node_Stack[Top]->Data;
delete BT_Node_Stack[Top];
}
return 1;
}
(5)中序線索樹類實現
#include<iostream>
using namespace std;
#define Link 0 //表示該節點有非空孩子節點
#define Thread 1 //表示該節點有後續節點(對於右子樹來說)
#define MAXSIZE 100
template<class T>
struct BT_Thread_Node
{
T Data;
BT_Thread_Node* Left_Child;
BT_Thread_Node* Right_Child;
int Ltag;
int Rtag;
};
template<class T>
class Thread_Binary_tree
{
private:
BT_Thread_Node<T>* Tree;
BT_Thread_Node<T>* Pre_Node;
BT_Thread_Node<T>* BT_Node_Stack[MAXSIZE];
int Create_Thread_BTree(BT_Thread_Node<T>* &Tree);
int Distory_Thread_BTree(BT_Thread_Node<T>* &Tree);
void InOrder_Thread_Op(BT_Thread_Node<T>* &Tree);
void _InOrder_Op(BT_Thread_Node<T>* &Tree);
public:
Thread_Binary_tree();
~Thread_Binary_tree();
void InOrder_Thread();
void _InOrder();
};
template<class T>
int Thread_Binary_tree<T>::Create_Thread_BTree(BT_Thread_Node<T>* &Tree)
{
int Data;
cin >> Data;
if (Data == -1)
Tree = NULL;
else
{
Tree = new BT_Thread_Node<T>;
Tree->Data = Data;
Tree->Ltag = Link;
Tree->Rtag = Link;
Create_Thread_BTree(Tree->Left_Child);
Create_Thread_BTree(Tree->Right_Child);
}
return 1;
}
template<class T>
int Thread_Binary_tree<T>::Distory_Thread_BTree(BT_Thread_Node<T>* &Tree)
{
if(Tree==NULL)
return 0;
int Top = -1;
BT_Thread_Node<T>* Cur_Node = Tree;
while (Cur_Node)
{
while (Cur_Node->Ltag == Link)
{
Cur_Node = Cur_Node->Left_Child;
}
BT_Node_Stack[++Top] = Cur_Node;
while (Cur_Node&&Cur_Node->Rtag == Thread)
{
Cur_Node = Cur_Node->Right_Child;
BT_Node_Stack[++Top] = Cur_Node;
}
Cur_Node = Cur_Node->Right_Child;
}
for (Top; Top != -1; Top--)
{
cout << BT_Node_Stack[Top]->Data;
delete BT_Node_Stack[Top];
}
return 1;
}
template<class T>
void Thread_Binary_tree<T>::InOrder_Thread_Op(BT_Thread_Node<T>* &Tree)
{
if (Tree == NULL) //空返回上一節點
return;
InOrder_Thread_Op(Tree->Left_Child); //左
if (Tree->Left_Child == NULL) //根
{
Tree->Ltag = Thread;
Tree->Left_Child = Pre_Node;
}
if (Pre_Node != NULL && Pre_Node->Right_Child == NULL)
{
Pre_Node->Rtag = Thread;
Pre_Node->Right_Child = Tree;
}
Pre_Node = Tree;
InOrder_Thread_Op(Tree->Right_Child); //右
}
template<class T>
void Thread_Binary_tree<T>::_InOrder_Op(BT_Thread_Node<T>* &Tree)
{
if (Tree == NULL)
return;
BT_Thread_Node<T>* Cur_Node = Tree;
while (Cur_Node) //當前節點不能為空
{
while (Cur_Node->Ltag == Link) //節點有左樹時,尋找最左端的樹
{
Cur_Node = Cur_Node->Left_Child;
}
cout << Cur_Node->Data << " ";
while (Cur_Node&&Cur_Node->Rtag == Thread) //節點非空並且右樹是線索樹時查詢最前的一個線索樹節點
{
Cur_Node = Cur_Node->Right_Child;
cout << Cur_Node->Data << " ";
}
Cur_Node = Cur_Node->Right_Child; //右樹不是線索樹,查詢該節點的右孩子節點
}
cout << endl;
}
template<class T>
Thread_Binary_tree<T>::Thread_Binary_tree():Pre_Node(NULL)
{
Create_Thread_BTree(Tree);
}
template<class T>
Thread_Binary_tree<T>::~Thread_Binary_tree()
{
Distory_Thread_BTree(Tree);
}
template<class T>
void Thread_Binary_tree<T>::InOrder_Thread()
{
InOrder_Thread_Op(Tree);
Pre_Node = NULL; //恢復值為空,便於下次線索化
}
template<class T>
void Thread_Binary_tree<T>::_InOrder()
{
_InOrder_Op(Tree);
}