1.為什麼要對二叉樹進行線索化?
一棵普通的二叉樹,只能找到該節點的左右孩子資訊,並不能知道該節點的直接前驅或後繼資訊。這種資訊只有在動態遍歷的過程中才能得到,因此我們引入線索二叉樹來儲存這些動態過程中得到的前驅和後繼的資訊。有n個節點的二叉樹,必定存在n+1個空指標域,我們可以充分利用這些空指標域,來儲存當前節點的前驅和後繼資訊。
在這裡,我們引入了兩個標誌位,link和thread,如果當前節點的左孩子為null,就讓當前節點指向該節點的前驅,否則繼續向下遍歷,知道左孩子為空結束,此時需要將該節點對應的標誌位改為thread;接下來遍歷該節點的右孩子,如果當前節點的右孩子為空,就讓該節點指向該節點的後繼,並將該節點的標誌位設定為thread
下來我們來研究一下節點的結構資訊
enum pointertag
;template
struct binarytreenodethd
};
相對於二叉樹的節點結構來說,線索二叉樹的節點結構裡增加了標誌位
線索化的概念:對二叉樹以某種順序進行遍歷使其變成線索二叉樹的過程叫做線索化。
2.構造線索化二叉樹
線索二叉樹構造的實質是在遍歷的時候將二叉鍊錶中的空指標指向改為指向該節點的前驅或者後繼,而前驅和後繼的資訊只有在遍歷的時候才能夠得到,為了記錄遍歷過程中訪問節點的先後順序,我們可以設定乙個prev指標來記錄上乙個剛剛訪問過的節點,root表示正在訪問的節點。以中序為例來分析一下線索化的過程:
中序的訪問順序是:先訪問左子樹,再訪問根節點,最後訪問右子樹
//中序線索化
(1)用prev表示上乙個剛剛出現過的節點,這樣就很容易找到該節點的前驅
(2)如何知道下乙個出現的節點?
在中序遍歷中,永遠不知道下乙個中序出現節點是誰,但是如果到達下一 個節點,一定知道上乙個出現的節點是誰,如果上乙個節點的右是空,說明上乙個節點的右需要線索化,讓上乙個節點的右指向當前節點 比如說,當到達節點2時,節點2的上乙個節點時節點3,節點3的右孩子為空,就讓節點3的右孩子指向節點2
下來看看用**如何實現
中序線索化
void _inorderthd(node* root,node*
&prev)//出現左樹為空或者右樹為空,就需要線索化
if (prev&&prev->_right==
null)
prev = root;
_inorderthd(root->_right,prev);
}
前序線索化
void _prevorderthd( node* root, node*
&prev)//前序線索化
if (prev&&prev->_right ==
null)//只有是link時才需要繼續走
prev = root;
if (root->_lefttag ==
link)//避免死迴圈
if (root->_righttag ==
link)
}
3.遍歷線索二叉樹由於對二叉樹進行了線索化,就有了節點前驅和後繼的資訊,這樣就很容易查詢當前節點的前驅和後繼資訊。
以中序為例:
查詢當前節點的前驅
(1)如果當前節點的_lefttag為thread,那麼當前節點指向該節點的前驅
(2)如果當前節點的_lefttag為link,,說明該節點有左子樹,那麼該節點的 前驅指向該節點左孩子的最右子樹
查詢當前節點的後繼
(1)如果當前節點的_righttag為thread,那麼當前節點指向該節點的後繼
(2)如果當前節點的_righttag為link,說明該節點有右孩子,該節點的後繼指向右孩子的最左節點
下來用**實現二叉樹的中序遍歷
//中序遍歷線索化,參考非遞迴,不需要定義棧
//中序遍歷,遇到一棵樹,先找這棵樹的最左節點,最先不被訪問節點一定是最左節點,
//當前節點訪問完成需要再訪問其右樹
//1、如果當前節點的右為link,當作子問題
//2.如果當前節點的左為thread,直接跳轉
//最後訪問的節點右樹一定是空
void inorderthread()//用線索化進行遍歷
cout << cur->_data <<
" ";
/*到這裡,cur的右樹還沒有被訪問
1、右為子樹
2、右為thredad,一定會跳到當前節點的父親
第一種寫法*/
//while (cur->_righttag==thread)
//子問題c,ur的右樹為link,子樹
//cur = cur->_right;
if (cur->_righttag ==
link)
else
cur = cur->_right;}}
cout << endl;
}
二叉樹前序、中序、後序線索化和遍歷的全部**
#pragma once
enum pointertag
;template
struct binarytreenodethd
};template
class binarytreethd
void prevorder()//前序遍歷
void prevorderthd()//前序線索化
void inorderthd()//中序線索化
//中序遍歷線索化,參考非遞迴,不需要定義棧
//中序遍歷,遇到一棵樹,先找這棵樹的最左節點,最先不被訪問節點一定是最左節點,
//當前節點訪問完成需要再訪問其右樹
//1、如果當前節點的右為link,當作子問題
//2.如果當前節點的左為thread,直接跳轉
//最後訪問的節點右樹一定是空
void inorderthread()//用線索化進行遍歷
cout
<< cur->_data << " ";
/*到這裡,cur的右樹還沒有被訪問
1、右為子樹
2、右為thredad,一定會跳到當前節點的父親
第一種寫法*/
//while (cur->_righttag==thread)
//子問題c,ur的右樹為link,子樹
//cur = cur->_right;
if (cur->_righttag == link)
else
cur = cur->_right;}}
cout
<< endl;
}protected:
node* _createtree(t* a, size_t n, const t& invalid, size_t &index)//index用引用,遞迴裡面++要給上一層使用
return root;
}//void _prevorder(node* root)//前序遍歷,注意線索化之後不能像之前那樣遍歷
//void _prevorder(node* root)//前序遍歷,注意線索化之後不能像之前那樣遍歷
if (root->_righttag == link)
}void _prevorderthd( node* root, node* &prev)//前序線索化
if (prev&&prev->_right == null)//只有是link時才需要繼續走
prev = root;
if (root->_lefttag == link)//避免死迴圈
if (root->_righttag == link)
}//中序線索化
//1、用prev表示上乙個出現過的節點
//2、如何知道下乙個出現的節點,在中序遍歷中,永遠不知道下乙個中序出現節點是誰,
//上乙個節點的右需要線索化,讓上乙個節點的右指向當前節點
void _inorderthd(node* root,node* &prev)//出現左樹為空或者右樹為空,就需要線索化
if (prev&&prev->_right==null)
prev = root;
_inorderthd(root->_right,prev);
}protected:
node *_root;
};//一棵樹只能被線索化成為一種順序
int main()
; binarytreethd t1(arr, sizeof(arr) / sizeof(arr[0]), '#');
t1.prevorder();
t1.inorderthd();
t1.inorderthread();
}
(C )二叉樹的線索化 線索二叉樹
線索化標誌tag enum pointertag 結點結構 template struct binarytreenodethd 基類迭代器 template struct binarytreeiterator t operator t operator bool operator const sel...
線索化二叉樹以及遍歷線索化二叉樹
1.線索二叉樹基本介紹 n個結點的二叉鍊錶中含有n 1 公式 2n n 1 n 1 個空指標域。利用二叉鍊錶中的空指標域,存放指向該結點在某種遍歷次序下的前驅和後繼結點的指標 這種附加的指標稱為 線索 這種加上了線索的二叉鍊錶稱為線索鍊錶,相應的二叉樹稱為線索二叉樹 threaded binaryt...
線索化二叉樹
define crt secure no warnings 1 includeusing namespace std enum pointertag 列舉 其結構如下 void prevorderthreading 前序 void postorderthreading 後序 void inorder...