二叉樹是一種非線性結構,遍歷二叉樹幾乎都是通過遞歸或者用棧輔助實現非遞歸的遍歷。用二叉樹作爲存儲結構時,取到一個節點,只能獲取節點的左孩子和右孩子,不能直接得到節點的任一遍歷序列的前驅或者後繼。
爲了保存這種在遍歷中需要的信息,我們利用二叉樹中指向左右子樹的空指針來存放節點的前驅和後繼信息。
enum PointerTag {THREAD, LINK};
template <class T>
struct BinaryTreeNode_Thd
{
T _data ; // 數據
BinaryTreeNode_Thd<T >* _left; // 左孩子
BinaryTreeNode_Thd<T >* _right; // 右孩子
PointerTag _leftTag ; // 左孩子線索標誌
PointerTag _rightTag ; // 右孩子線索標誌
};
前序線索化以及遍歷:
void PrevOrderThreading()//前序線索化
{
Node * prev = NULL;
_PrevOrderThreading(_root, prev);
}
void PrevOrderThd()//前序遍歷
{
_PrevOrderThd(_root);
cout << "over" << endl;
}
//前序線索化
//prev必須要保證進入下一次遞歸時,保留上次遞歸的cur值,所以用“引用”
void _PrevOrderThreading(Node * root,Node * & prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
//線索化前驅
if (cur->_left == NULL)
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev&&prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
prev = cur;
//遞歸線索化左子樹
if (cur->_leftTag == LINK)
{
_PrevOrderThreading(cur->_left, prev);
}
//遞歸線索化右子樹
if (cur->_rightTag == LINK)
{
_PrevOrderThreading(cur->_right, prev);
}
}
//前序遍歷
void _PrevOrderThd(Node *root)
{
Node * cur = root;
while (cur)
{
while (cur->_leftTag == LINK)
{
cout << cur->_data << "->";
cur = cur->_left;
}
cout << cur->_data << "->";
//當存在右線索時,跳轉過去
while (cur->_rightTag == THREAD)
{
cur = cur->_right;
cout << cur->_data << "->";
}
//該節點存在左子樹時,跳到左子樹上遍歷
if (cur->_leftTag == LINK)
{
cur = cur->_left;
}
else
{
cur = cur->_right;
}
}
}
//前序遍歷(2)
//void _PrevOrderThd(Node *root)
//{
// Node * cur = root;
// while (cur)
// {
// while (cur->_leftTag == LINK)
// {
// cout << cur->_data << "->";
// cur = cur->_left;
// }
// cout << cur->_data << "->";
// //直接看作是根節點,重新進行遍歷
// cur = cur->_right;
// }
//}
中序線索化以及中序遍歷:
void InOrderThreading()//中序線索化
{
Node * prev = NULL;
_InOrderThreading(_root, prev);
}
void InOrderThd()//中序遍歷
{
_InOrderThd(_root);
cout << "over" << endl;
}
//中序線索化
void _InOrderThreading(Node *root, Node * &prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
_InOrderThreading(cur->_left, prev);//遞歸cur->_left==NULL的cur節點
if (cur->_left == NULL)//線索化前驅
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev&&prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
//更新prev
prev = cur;
_InOrderThreading(cur->_right, prev);
}
//中序遍歷
void _InOrderThd(Node *root)
{
Node * cur = root;
while (cur)
{
//直接找到最左最下邊的節點
while (cur->_leftTag == LINK)
{
cur = cur->_left;
}
cout << cur->_data << "->";
//當該節點存在右線索時,可能存在連續後繼
while (cur->_rightTag == THREAD)
{
cur = cur->_right;
cout << cur->_data << "->";
}
cur = cur->_right;
}
}
後序線索化:
void PostOrderThreading()//後序線索化
{
Node * prev = NULL;
_PostOrderThreading(_root, prev);
}
//後序線索化
void _PostOrderThreading(Node *root, Node * &prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
_PostOrderThreading(cur->_left, prev);//最左結點
_PostOrderThreading(cur->_right, prev);//最右結點
if (cur->_left == NULL)//線索化前驅
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev && prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
//更新prev
prev = cur;
}
測試代碼以及結果:
void Test()
{
int array[10] = { 1, 2, 3, '#', '#', 4, '#' , '#', 5, 6 };
BinaryTreeThd<int> b1(array, 10, '#');
BinaryTreeThd<int> b2(array, 10, '#');
b1.PrevOrderThreading();
cout << "前序輸出:";
b1.PrevOrderThd();
b2.InOrderThreading();
cout << "中序輸出:";
b2.InOrderThd();
}
最後,附上全部代碼:
#pragma once
#include <iostream>
using namespace std;
enum PointerTag
{
THREAD,//有線索標誌
LINK //無線索標誌
};
template <typename T>
struct BinaryTreeNodeThd
{
public:
T _data; //數據
BinaryTreeNodeThd<T> * _left; //左孩子
BinaryTreeNodeThd<T> * _right; //右孩子
PointerTag _leftTag; //左孩子線索標誌
PointerTag _rightTag; //右孩子線索標誌
public:
BinaryTreeNodeThd(const T a)
:_data(a)
,_left(NULL)
,_right(NULL)
,_leftTag(LINK)
,_rightTag(LINK)
{}
};
template <typename T>
class BinaryTreeThd
{
typedef BinaryTreeNodeThd<T> Node;
public:
BinaryTreeThd()//無參構造函數
:_root(NULL)
{}
BinaryTreeThd(const T* a, size_t size, const T & invalid)//有參構造函數
:_root(NULL)
{
size_t index = 0;
_root = _CheckTree(a, size, invalid, index);
}
void PrevOrderThreading()//前序線索化
{
Node * prev = NULL;
_PrevOrderThreading(_root, prev);
}
void InOrderThreading()//中序線索化
{
Node * prev = NULL;
_InOrderThreading(_root, prev);
}
void PostOrderThreading()//後序線索化
{
Node * prev = NULL;
_PostOrderThreading(_root, prev);
}
void PrevOrderThd()//前序遍歷
{
_PrevOrderThd(_root);
cout << "over" << endl;
}
void InOrderThd()//中序遍歷
{
_InOrderThd(_root);
cout << "over" << endl;
}
protected:
//創建二叉樹
Node * _CheckTree(const T* a, size_t size, const T& invalid, size_t & index)
{
Node * root = NULL;
if (index < size&&a[index] != invalid)//不能越界
{
root= new Node(a[index]);//創建節點
root->_left = _CheckTree(a, size, invalid, ++index);
root->_right = _CheckTree(a, size, invalid, ++index);
}
return root;
}
//前序線索化
//prev必須要保證進入下一次遞歸時,保留上次遞歸的cur值,所以用“引用”
void _PrevOrderThreading(Node * root,Node * & prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
//線索化前驅
if (cur->_left == NULL)
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev&&prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
prev = cur;
//遞歸線索化左子樹
if (cur->_leftTag == LINK)
{
_PrevOrderThreading(cur->_left, prev);
}
//遞歸線索化右子樹
if (cur->_rightTag == LINK)
{
_PrevOrderThreading(cur->_right, prev);
}
}
//前序遍歷
void _PrevOrderThd(Node *root)
{
Node * cur = root;
while (cur)
{
while (cur->_leftTag == LINK)
{
cout << cur->_data << "->";
cur = cur->_left;
}
cout << cur->_data << "->";
//當存在右線索時,跳轉過去
while (cur->_rightTag == THREAD)
{
cur = cur->_right;
cout << cur->_data << "->";
}
//該節點存在左子樹時,跳到左子樹上遍歷
if (cur->_leftTag == LINK)
{
cur = cur->_left;
}
else
{
cur = cur->_right;
}
}
}
//前序遍歷(2)
//void _PrevOrderThd(Node *root)
//{
// Node * cur = root;
// while (cur)
// {
// while (cur->_leftTag == LINK)
// {
// cout << cur->_data << "->";
// cur = cur->_left;
// }
// cout << cur->_data << "->";
// //直接看作是根節點,重新進行遍歷
// cur = cur->_right;
// }
//}
//中序線索化
void _InOrderThreading(Node *root, Node * &prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
_InOrderThreading(cur->_left, prev);//遞歸cur->_left==NULL的cur節點
if (cur->_left == NULL)//線索化前驅
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev&&prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
//更新prev
prev = cur;
_InOrderThreading(cur->_right, prev);
}
//中序遍歷
void _InOrderThd(Node *root)
{
Node * cur = root;
while (cur)
{
//直接找到最左最下邊的節點
while (cur->_leftTag == LINK)
{
cur = cur->_left;
}
cout << cur->_data << "->";
//當該節點存在右線索時,可能存在連續後繼
while (cur->_rightTag == THREAD)
{
cur = cur->_right;
cout << cur->_data << "->";
}
cur = cur->_right;
}
}
//後序線索化
void _PostOrderThreading(Node *root, Node * &prev)
{
Node * cur = root;
if (cur == NULL)
{
return;
}
_PostOrderThreading(cur->_left, prev);//最左結點
_PostOrderThreading(cur->_right, prev);//最右結點
if (cur->_left == NULL)//線索化前驅
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
//線索化後繼
//cur是當前節點,線索化前一個節點的後繼,指向cur
if (prev && prev->_right == NULL)
{
prev->_rightTag = THREAD;
prev->_right = cur;
}
//更新prev
prev = cur;
}
protected:
Node * _root;
};
void Test()
{
int array[10] = { 1, 2, 3, '#', '#', 4, '#' , '#', 5, 6 };
BinaryTreeThd<int> b1(array, 10, '#');
BinaryTreeThd<int> b2(array, 10, '#');
b1.PrevOrderThreading();
cout << "前序輸出:";
b1.PrevOrderThd();
b2.InOrderThreading();
cout << "中序輸出:";
b2.InOrderThd();
}
本文出自 “不斷進步的空間” 博客,請務必保留此出處http://10824050.blog.51cto.com/10814050/1770408