所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。
一、先序遍历
递归方式实现:
- 访问根结点(此处的根结点并非特指整棵树的根结点,泛着所有子树的根节点 );
- 递归访问左孩子;
- 递归访问右孩子;
void PreOrder(BtNode *p)//前序遍历
{
if (p != NULL)
{
cout << p->data << " ";//此处相当于每颗子树的根结点;
PreOrder(p->leftchild);
PreOrder(p->rightchild);
}
}
非递归方式实现:通过压栈和出栈的方式;
方法一:从右到左入栈(可以联想两个点:栈的性质是先进后出+先序遍历的递归顺序)
- 将根结点入栈;
- 获取栈顶元素并打印;
- 栈顶元素出栈;
- 判断获取到的栈顶元素右子树是否为空,不为空则入栈右节点;
- 判断获取到的栈顶元素左子树是否为空,不为空则入栈左节点;
void Nice_PreOrder1(BtNode* tree)//非递归先序遍历,从右到左入栈法则。
{
stack<BtNode*>st;
st.push(tree);
while (!st.empty())
{
BtNode* p = st.top();
cout << p->data << " ";
st.pop();
if (p->rightchild != NULL)
{
st.push(p->rightchild);
}
if (p->leftchild != NULL)
{
st.push(p->leftchild);
}
}
}
方法二:从左至右入栈
- 利用一个栈,首先从根节点一直入栈左子树,并不断打印遇到的节点,直到左子树为NULL
- 获取并出栈栈顶元素,对获取到的栈顶元素的右子树做如上操作
- 直到将栈为空,遍历结束
void Nice_PreOrder2(BtNode *tree)//非递归先序遍历,从左到右栈法则
{
if (tree == NULL)
{
cout << "This is empty tree";
}
stack<BtNode *>sta;
while (tree || !sta.empty())
{
while (tree)
{
sta.push(tree);
cout << tree->data<<" ";
tree = tree->leftchild;
}
tree = sta.top();
sta.pop();
tree = tree->rightchild;
}
}
二、中序遍历
递归方式实现:
- 递归遍历左子树;
- 访问根结点;
- 递归遍历右子树;
void InOrder(BtNode *p)//中序遍历
{
if (p != NULL)
{
InOrder(p->leftchild);
cout << p->data << " ";
InOrder(p->rightchild);
}
}
非递归方式实现:
方法一:
很显然中序遍历是当第二次访问到某个节点时打印(或其他操作),因此可以利用出栈的次数来进行打印。
首先定义一个存储节点访问次数的结构体,此时栈里存储的不光是节点,还有节点对应的出栈次数。
typedef struct stkNode
{
BtNode* node;//节点
int num;//节点对应的出栈次数
public:
stkNode(BtNode* n)
{
node = n;
num = 0;
}
}stkNode;
- 首先入栈根节点。
- 获取栈顶元素,判断如果该节点出栈次数,如果为2则打印,然后判断右子树是否为NULL,不为空入栈右子树
- 如果该节点出栈次数不为2则继续将该节点入栈,然后判断该节点左子树是否为NULL,不为空则入栈左子树
- 直到栈空,否则重复上述操作
void Nice_InOrder2(BtNode* tree)
{
if (tree == NULL)
{
return;
}
stack<stkNode> st;
st.push(tree);
while (!st.empty())
{
stkNode stk = st.top();
st.pop();
if (++stk.num == 2)
{
cout << stk.node->data << " ";
if (stk.node->rightchild != NULL)
{
st.push(stkNode(stk.node->rightchild));
}
}
else
{
st.push(stk);
if (stk.num == 1 && stk.node->leftchild != NULL)
{
st.push(stkNode(stk.node->leftchild));
}
}
}
cout << endl;
}
方式二:该方法和从左到右入栈先序非递归遍历相似,只是在入栈左子树的过程中不打印所遇到的节点,而是等到出栈的时候再打印。思路如下
- 利用一个栈,首先从根节点一直入栈左子树,直到左子树为NULL;
- 获取并出栈栈顶元素并打印,对获取到的栈顶元素的右子树做如上操作;
- 直到将栈为空,遍历结束
void Nice_InOrder1(BtNode* tree)//非递归中序遍历,一直入左,左空打印,入当前右。
{
if (tree == NULL)
{
cout << "This is empty tree";
}
stack<BtNode *>sta;
while (tree || !sta.empty())
{
while (tree)
{
sta.push(tree);
tree = tree->leftchild;
}
tree = sta.top();
cout << tree->data << " ";
sta.pop();
tree = tree->rightchild;
}
}
三、后序遍历
递归方式实现:
- 先递归遍历左子树
- 再递归遍历右子树
- 最后打印当前节点
void PastOrder(BtNode *p)//后序遍历
{
if (p != NULL)
{
PastOrder(p->leftchild);
PastOrder(p->rightchild);
cout << p->data << " ";
}
}
非递归方式实现:利用出栈次数的方式来做,只不过后序遍历是第三次出栈进行打印。
void Nice_PastOrder(BtNode* tree)//非递归后续遍历。
{
if (tree == NULL)
{
return;
}
stack<stkNode> st;
st.push(tree);
while (!st.empty())
{
stkNode stk = st.top();
st.pop();
if (++stk.num == 3)
{
cout << stk.node->data << " ";
}
else
{
st.push(stk);
if (stk.num == 1 && stk.node->leftchild != NULL)
{
st.push(stk.node->leftchild);
}
else if (stk.num == 2 && stk.node->rightchild != NULL)
{
st.push(stk.node->rightchild);
}
}
}
cout << endl;
}
四、层次遍历
所谓层次遍历就是按照每一层依次取打印;
第一步:先利用递归的思想打印第k层;
第二步:求树的深度;
第三步:循环打印每一层;
int GetDepth(BtNode *tree)//层次遍历中--获得深度
{
if (tree == NULL)
{
return 0;
}
else
{
return max(GetDepth(tree->leftchild), GetDepth(tree->rightchild)) + 1;
}
}
void Level_Order_K(BtNode *tree, int k)//第k层的遍历
{
if (!tree||k<0) return ;
if (k == 0)
{
cout << tree->data<<" ";
}
if (k>0)
{
Level_Order_K(tree->leftchild, k - 1);
Level_Order_K(tree->rightchild, k - 1);
}
}
void Level_Print(BtNode* tree)//所有层的打印
{
int depth = GetDepth(tree);
for (int i = 0; i < depth; i++)
{
Level_Order_K(tree, i);
cout << endl;
}
}