【資料結構】二叉樹的遍歷及應用
前言
在二叉樹的應用中,常常要求在樹中查詢某些結點,或者對樹中的結點統一進行某種處理。因此,就提到了二叉樹的遍歷問題,對於線性結構來說,遍歷是一個很容易解決的問題,而二叉樹偏偏是一種非線性的結構,因此需要尋找一種規律。
二叉樹由三個基本單元組成,分別是根結點、左子樹及右子樹。依次遍歷這三個部分就能遍歷整個二叉樹,以V、L、R表示訪問根結點、遍歷左子樹及遍歷右子樹,則有VLR、VRL、RLV、RVL、LVR、LRV六種遍歷二叉樹的方案。若規定左子樹一定先於右子樹被遍歷,就只剩下三種情況。再根據根結點被訪問的次序,可以分為可以分別命名為先(根)序遍歷,中(根)序遍歷,後(根)序遍歷。
為了方便理解,定義一個二叉樹的結點型別和二叉樹型別;
template<class T> class BinaryTree; template<class T> class BinaryTreeNode { friend class BinaryTree<T>; public: BinaryTreeNode(); BinaryTreeNode(T D, BinaryTreeNode <T> *L=NULL, BinaryTreeNode <T> *R=NULL) { data = D; Lchild = L; Rchild = R; } private: T data; BinaryTreeNode <T> *Lchild ; BinaryTreeNode <T> *Rchild ; }; template<class T> class BinaryTree { public: BinaryTree(); BinaryTree(T D, BinaryTreeNode <T> *L=NULL, BinaryTreeNode <T> *R=NULL) { root = new BinaryTreeNode<T>(D, L, R); } BinaryTree(BinaryTreeNode <T> *Node) { root = Node; } void PreOrder(); void PreOrder(BinaryTreeNode <T> *current); void InOrder(); void InOrder(BinaryTreeNode <T> *current); void PastOrder(); void PastOrder(BinaryTreeNode <T> *current); private: BinaryTreeNode <T> *root=NULL; };
構建一個如下圖的二叉樹;
二叉樹的遍歷
先序遍歷
先序遍歷的規則如下:若當前二叉樹為空,則返回空,否則
1. 訪問根結點;
2. 先序遍歷左子樹;
3. 先序遍歷右子樹;
上圖中的二叉樹的先序遍歷為:ABDEHIJKCFG
根據上面的關係,可以寫出二叉樹類的先序遍歷的函式;
template<class T> void BinaryTree<T>::PreOrder() { cout << "先序遍歷:"; PreOrder(root); //先序遍歷 } template<class T> void BinaryTree<T>::PreOrder(BinaryTreeNode <T> *current) { if (current != NULL) //當current為空指標,說明已經到達葉結點 { cout << current->data<<" "; //首先輸出當前結點的值 PreOrder(current->Lchild); //遞迴呼叫左子樹 PreOrder(current->Rchild); //遞迴呼叫右子樹 } }
中序遍歷
中序遍歷的規則如下:若當前二叉樹為空,則返回空,否則
1. 中序列根結點的左子樹;
2. 訪問根結點;
3. 中序遍歷根結點的右子樹;
上圖中的二叉樹的中序遍歷為:DBHEJIKAFCG
根據上面的關係,可以寫出二叉樹類的中序遍歷的函式;
template<class T>
void BinaryTree<T>::InOrder()
{
InOrder(root); //先序遍歷
}
template<class T>
void BinaryTree<T>::InOrder(BinaryTreeNode <T> *current)
{
if (current != NULL) //當current為空指標,說明已經到達葉結點
{
InOrder(current->Lchild); //遞迴呼叫左子樹
cout << current->data << " "; //首先輸出當前結點的值
InOrder(current->Rchild); //遞迴呼叫右子樹
}
}
後序遍歷
後序遍歷的規則如下:若當前二叉樹為空,則返回空,否則
1. 後序列根結點的左子樹;
2. 後序遍歷根結點的右子樹;
3. 訪問根結點;
上圖中的二叉樹的後序遍歷為:DHJKIEBFGCA
根據上面的關係,可以寫出二叉樹類的後序遍歷的函式;
template<class T>
void BinaryTree<T>::PastOrder()
{
PastOrder(root); //先序遍歷
}
template<class T>
void BinaryTree<T>::PastOrder(BinaryTreeNode <T> *current)
{
if (current != NULL) //當current為空指標,說明已經到達葉結點
{
PastOrder(current->Lchild); //遞迴呼叫左子樹
PastOrder(current->Rchild); //遞迴呼叫右子樹
cout << current->data << " "; //首先輸出當前結點的值
}
}
二叉樹遍歷的應用
計算節點個數
計算二叉樹的節點的格式可以利用二叉樹的遍歷,常用的是後遍歷,先遍歷根結點的左子樹和右子樹,分別計算出左右子樹的結點個數,然後加上根結點個數就是整個二叉樹節點個數。
template<class T>
int BinaryTree<T>::size(BinaryTreeNode <T> *current)
{
if (current == NULL){ return 0; }
else{ return 1 + size(current->Lchild) + size(current->Rchild); }
}
計算二叉樹的高度
與計算二叉樹節點高度類似,計算二叉樹高度時如果高度為0,返回-1;否則按照後序遍歷規則,先遞迴計算根結點的左子樹和右子樹的高度,再求兩者中的較大者,並加1,最終得到整個二叉樹的高度;
template<class T>
int BinaryTree<T>::depth(BinaryTreeNode <T> *current)
{
if (current == NULL){ return -1; }
else{ return 1 + Max(depth(current->Lchild), depth(current->Rchild)); }
}
知道先序(後序)和中序求二叉樹後序(先序)
有一些題目喜歡提這樣的問題,以知道先序和中序求後序為例,例如已知先序是ABDEHIJKCFG,已知中序是DBHEJIKAFCG,求二叉樹的後序排列。(知道先序和後序是無法求出中序的)
其實瞭解二叉樹的遍歷後,這個題目很簡單。由於先序是先遍歷根結點,先序排列的第一個點必定根結點,也就是說A是根結點;再看中序遍歷,先遍歷左子樹,左子樹遍歷玩才會遍歷根結點,因此,排在A前方的全是左子樹上的點,排在A後方的全是,如果A在中序排列中也是排在第一個,說明它沒有左子樹。因此有了如下結構;
再看左子樹,此時左子樹的先序為BDEHIJK,中序為DBHEJIK。同樣的道理,B為A的左子樹的根結點,中序排列中在B前面的為左子樹,排在B後側的為右子樹;如此反覆進行就能得出二叉樹的結構,再進行後序遍歷就能得出後序排列。
當知道後序和中序排列求先序排列時,也是同樣的道理,二叉樹的根結點是最後被遍歷到的點。
根據上面的關係,可以的寫出重建二叉樹的函式;
template<class T>
BinaryTreeNode<T>* BinaryTreeNode<T>::reConstructBinaryTree(vector<T> pre, vector<T> in)
{
BinaryTreeNode<T> *BiTree=NULL;
int size = pre.size();
if (size != 0)
{
BiTree->data = pre[0]; //根結點賦值
//構建左右子樹的序列;
vector<T> leftPre;
vector<T> leftIn;
vector<T> rightPre;
vector<T> rightIn;
//在中序排列中找到根結點的位置
int i = 0;
for (; i<size; i++)
{
if (in[i] == pre[0])
{
break;
}
}
for (int j = 0; j < size; j++)
{
if (j<i) //中序序列:排在根結點之前的放入左子樹
{
if (j != i)
{
leftIn.push_back(in[j]);
}
}
if (j>i) //中序序列:排在根結點之後的放入右子樹
{
rightIn.push_back(in[j]);
}
}
for (int j = 1; j < size; j++)
{
if (j <= i) //先序序列:排在根結點之前的放入左子樹
{
leftPre.push_back(pre[j]);
}
if (j>i) //先序序列:排在根結點之後的放入右子樹
{
rightPre.push_back(pre[j]);
}
}
if (leftIn.size() != 1){ BiTree->Lchild = reConstructBinaryTree(leftPre, leftIn); }
if (rightIn.size() != 1){ BiTree->Rchild = reConstructBinaryTree(rightPre, rightIn); }
}
return BiTree;
}
最後
二叉樹的應用非常多,例如堆排序,二叉排序樹,霍夫曼樹等等,需要更多地去了解。
已完。