【c++】二叉樹的線索化
什麼是二叉樹的線索化?或者問什麼是線索二叉樹?
按照某種遍歷方式對二叉樹進行遍歷,可以把二叉樹中所有結點排序為一個線性序列。在改序列中,除第一個結點外每個結點有且僅有一個直接前驅結點;除最後一個結點外每一個結點有且僅有一個直接後繼結點。這些指向直接前驅結點和指向直接後續結點的指標被稱為線索(Thread),加了線索的二叉樹稱為線索二叉樹。
以上是搜狗百科的一段文字,反正我是沒看太懂。簡單點,以我的理解,線索二叉樹就是充分利用了二叉樹結點中的空指標,讓它們分別指向本結點的前驅或者後繼。既充分利用了資源,又可以讓我們方便遍歷這棵樹。
什麼是二叉樹結點的空指標?
我們先看一棵樹。圖中的結點3,4,6的左右指標,結點5的右指標等類似指標都為空指標。
\
什麼又叫讓這些空指標指向本節點的前驅或者後繼呢?
這個問題分為3中情況,前序,中序,後序。例如上圖中的樹前序遍歷序列為1,2,3,4,5,6。這樣,我們就可以讓結點3的左指標指向它的前驅2,結點3的右指標指向它的後繼4。讓結點4的左指標指向4的前驅3,讓結點4的右指標指向4的後繼5。結點5的左不為空,所以不操作。結點5的右為空,讓結點5的右指向5的後繼6。結點6的左右都為空,讓結點6的左指標指向6的前驅5,因為6為最後一個元素,6的後繼為空,所以讓結點6的右指標指向空。
讓我們把指標重置後的圖畫出來:
類似的,後序與中序我就不再細說,這裡把圖解給大家,一看便知。
我建議大家親自動手把這3個圖畫一下,有奇效!!!!!!!
道理大家都懂了,那麼程式碼該如何寫呢? 答案:遞迴。
線索二叉樹的結點定義和普通二叉樹的結點定義不一樣,我們額外需要兩個標誌_leftTag和_rightTag。
我們用Type型別的_leftTag來標識左指標,當_leftTag等於THREAD時,表明這個指標已經被線索化(例圖中紫色指標)。當_leftTag等於LINK時,表明這個指標沒有被線索化,是普通的二叉樹指標(例圖中紅色指標)。enum Type { THREAD,//表示指標被線索化 LINK//表示指標未被線索化 }; template<typename T> struct BinaryTreeNode { T _data; BinaryTreeNode<T> *_left; BinaryTreeNode<T> *_right; Type _leftTag;//標識左指標 Type _rightTag;//標識右指標 BinaryTreeNode(const T& x)//建構函式 :_data(x) , _left(NULL) , _right(NULL) , _leftTag(LINK) , _rightTag(LINK) {} BinaryTreeNode()//預設建構函式 {} };
因為線上索化過程中,我們需要讓當前結點的前驅指向當前結點,所以我們需要設立一個prev來儲存上一次訪問的結點。這個prev要麼設定成全域性變數,要麼設定成區域性靜態變數,切記!
遞迴程式碼:
前序
//前序線索化
void _PrevOrder_Thd(Node* _root)
{
static Node* prev = NULL;
if (_root)
{
if (!_root->_left)
{
_root->_leftTag = THREAD;
_root->_left = prev;
}
if (prev && !prev->_right)
{
prev->_rightTag = THREAD;
prev->_right = _root;
}
prev = _root;
if (_root->_leftTag == LINK)
_PrevOrder_Thd(_root->_left);
if (_root->_rightTag == LINK)
_PrevOrder_Thd(_root->_right);
}
}
中序
//中序線索化
void _InOrder_Thd(Node* _root)
{
static Node* prev = NULL;
if (_root)
{
if (_root->_leftTag == LINK)
{
_InOrder_Thd(_root->_left);
}
if (!_root->_left)
{
_root->_leftTag = THREAD;
_root->_left = prev;
}
if (prev && !prev->_right)
{
prev->_rightTag = THREAD;
prev->_right = _root;
}
prev = _root;
if (_root->_rightTag == LINK)
{
_InOrder_Thd(_root->_right);
}
}
}
後序
//後序線索化
void _PostOrder_Thd(Node* _root)
{
if (_root == NULL)
{
return;
}
static Node* prev = NULL;
_PostOrder_Thd(_root->_left);
_PostOrder_Thd(_root->_right);
if (!_root->_left)
{
_root->_leftTag = THREAD;
_root->_left = prev;
}
if (prev && !prev->_right)
{
prev->_rightTag = THREAD;
prev->_right = _root;
}
prev = _root;
}
最後,來說一下今年的一道面試題,如何將一個二叉樹轉化成一個有序的雙向連結串列?
在你沒學線索化之前,這道題可能很麻煩。但是現在不同了,利用中序線索化的思想可以很快將這道題解出來!
//利用中序線索化思想將搜尋二叉樹轉換成有序的雙向連結串列
void _TreeToList(Node* _root)
{
static Node* prev = NULL;//設立prev儲存上一次訪問的結點
if (_root == NULL)//如果根結點為空,表明樹空,直接返回.
{
return;
}
_TreeToList(_root->_left);//遞迴左子樹
_root->_left = prev;//讓當前結點的左指標指向上一次訪問的結點,即前驅。
if (prev)
{
prev->_right = _root;//讓prev指向當前結點,構成雙向
}
prev = _root;//更新prev
_TreeToList(_root->_right);//遞迴右子樹
}
有哪裡寫的不對請大家指出來,不明白的也可以留言問我,互相學習!