二叉树作为一个常见数据结构,就不细说其具体结构了(不知道先面壁去:)),在实际工作中很常见,在面试过程中也会经常被涉及到,一些排序算法的基础也是二叉树。
二叉树的遍历,一般实现方法是用递归,算法逻辑简单直观。而非递归的算法就是手动压栈出栈的步骤来实现的,百度中搜索出来的算法描述理解起来都有些困难,遂花了将近一天时间把非递归算法研究了一遍,并用C代码加以简单实现。
二叉树遍历算法分三种,先序遍历、中序遍历和后序遍历。
1 基本数据结构:
struct TreeNode
{
struct TreeNode* m_pLeftChild;
struct TreeNode* m_pRightChild;
bool m_bStatus; // 是否已经被访问
char m_strData[128];
};
2 简单栈的实现:
struct TreeNode* g_NodeStack[1024];
int g_StackPointer;
void InitStack()
{
for (int i = 0; i < 1024; i++)
{
g_NodeStack[i] = NULL;
}
g_StackPointer = -1;
}
struct TreeNode* StackTop()
{
if(g_StackPointer < 0)
{
printf("\nStack is empty.\n");
return NULL;
}
else
{
return g_NodeStack[g_StackPointer];
}
}
bool PushStack(struct TreeNode* pNode_)
{
if ( (g_StackPointer < 1024 - 1) && (g_StackPointer >=-1) )
{
g_StackPointer++;
g_NodeStack[g_StackPointer] = pNode_;
return true;
}
else
{
printf("\nThe stack memory space is already full.\n");
return false;
}
}
bool PopStack()
{
if ( (g_StackPointer < 1024) && (g_StackPointer >=0) )
{
g_NodeStack[g_StackPointer] = NULL;
g_StackPointer--;
return true;
}
else if(-1>=g_StackPointer)
{
printf("\nThe stack is under range.\n");
return false;
}
else
{
printf("\nThe stack pointer is upper range.\n");
return false;
}
}
3 二叉树初始化: 实现一颗简单二叉树的初始化,仅用于验证算法
void FillNode(struct TreeNode* pNode_, char* strData_)
{
pNode_->m_pLeftChild = NULL;
pNode_->m_pRightChild = NULL;
pNode_->m_bStatus = false;
ZeroMemory(pNode_->m_strData, sizeof(pNode_->m_strData));
copy(strData_, strData_ + strlen(strData_), pNode_->m_strData);
}
void FillNode(struct TreeNode* pNode_, char* strData_,
struct TreeNode* pLeftChild_, struct TreeNode* pRightChild_)
{
pNode_->m_pLeftChild = pLeftChild_;
pNode_->m_pRightChild = pRightChild_;
pNode_->m_bStatus = false;
ZeroMemory(pNode_->m_strData, sizeof(pNode_->m_strData));
copy(strData_, strData_ + strlen(strData_), pNode_->m_strData);
}
struct TreeNode* TreeInit()
{
struct TreeNode* pRoot = (struct TreeNode*)malloc(sizeof(struct TreeNode));
FillNode(pRoot, "A");
struct TreeNode* ppLevel1Node[2] = {NULL, NULL};
for (int i = 0; i < 2; i++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('B' + i);
FillNode(pNode, strData);
ppLevel1Node[i] = pNode;
}
pRoot->m_pLeftChild = ppLevel1Node[0];
pRoot->m_pRightChild = ppLevel1Node[1];
struct TreeNode* ppLevel2Node[4] = {NULL, NULL, NULL, NULL};
int j = 0;
for (j = 0; j < 4; j++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('D' + j);
FillNode(pNode, strData);
ppLevel2Node[j] = pNode;
}
for (j = 0; j < 4; j+=2)
{
ppLevel1Node[j/2]->m_pLeftChild = ppLevel2Node[j];
ppLevel1Node[j/2]->m_pRightChild = ppLevel2Node[j + 1];
}
struct TreeNode* ppLevel3Node[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
int k = 0;
for (k = 0; k < 8; k++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('H' + k);
FillNode(pNode, strData);
ppLevel3Node[k] = pNode;
}
for (k = 2; k < 8; k+=2)
{
ppLevel2Node[k/2]->m_pLeftChild = ppLevel3Node[k];
ppLevel2Node[k/2]->m_pRightChild = ppLevel3Node[k + 1];
}
return pRoot;
}
4 先序遍历:
所谓先序遍历,其遍历顺序是当前节点->左子节点->右子节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,并取当前节点为栈顶节点,visit当前节点并标记为已访问节点
(3)弹出栈顶节点
(4)若当前节点有右子节点且右子节点为未访问节点,将右子节点压栈
(5)若当前节点有左子节点且左子节点为未访问节点,将左子节点压栈
(6)跳到第(2)步
(7)结束
代码见示意:
void PreOrderVisit(struct TreeNode* pRoot_)
{
printf("PreOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
// visit
printf("%s ", pNode->m_strData);
pNode->m_bStatus = true;
PopStack();
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PushStack(pNode->m_pRightChild);
}
if (pNode->m_pLeftChild && !pNode->m_pLeftChild->m_bStatus)
{
PushStack(pNode->m_pLeftChild);
}
}
}
5 中序遍历:
中序遍历,其遍历顺序是左子节点->当前节点->右子节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,设当前节点为栈顶节点
(3)若当前节点左子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
(4)若当前节点左子节点不为NULL且左子节点为已访问状态,则visit当前节点并标记为已访问,弹出栈顶节点
(5)若当前节点左子节点不为NULL且左子节点为未访问状态
5.1 若当前节点右子节点不为空,则弹出当前节点,压入右子节点,再压入当前节点
5.2 压入左子节点
(6)跳到第(2)步
(7)结束
代码见示意:
void InOrderVisit(struct TreeNode* pRoot_)
{
printf("InOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
if (!pNode->m_pLeftChild)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if (pNode->m_pLeftChild->m_bStatus)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else
{
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PopStack();
PushStack(pNode->m_pRightChild);
PushStack(pNode);
}
PushStack(pNode->m_pLeftChild);
}
}
}
6 后序遍历
后序遍历,其遍历顺序是左子节点->右子节点->当前节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,设当前节点为栈顶节点
(3)对当前节点作判定处理:
3.1 若左子节点为NULL且右子节点为NULL,则则visit当前节点并标记为已访问,弹出栈顶节点
3.2 若左子节点不为NULL且已被访问 且右子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
3.3 若右子节点不为NULL且已被访问 且左子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
3.4 若左子节点不为NULL且已被访问 且 右子节点不为NULL且已被访问在,则visit当前节点并标记为已访问,弹出栈顶节点
(4)若右子节点不为NULL且右子节点未被访问,右子节点入栈
(5)若左子节点不为NULL且左子节点未被访问,左子节点入栈
(6)跳到第(2)步
(7)结束
代码见示意:
void PostOrderVisit(struct TreeNode* pRoot_)
{
printf("PostOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
if (!pNode->m_pLeftChild && !pNode->m_pRightChild)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if( !pNode->m_pRightChild && (pNode->m_pLeftChild && pNode->m_pLeftChild->m_bStatus) )
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if( !pNode->m_pLeftChild && (pNode->m_pRightChild && pNode->m_pRightChild->m_bStatus) )
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if ( (pNode->m_pRightChild && pNode->m_pRightChild->m_bStatus)
&& (pNode->m_pLeftChild && pNode->m_pLeftChild->m_bStatus))
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PushStack(pNode->m_pRightChild);
}
if (pNode->m_pLeftChild && !pNode->m_pLeftChild->m_bStatus)
{
PushStack(pNode->m_pLeftChild);
}
}
}
补充测试的main函数:
#include <stdio.h>
#include <windows.h>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[])
{
struct TreeNode* pRoot = NULL;
pRoot = TreeInit();
//PreOrderVisit(pRoot);
//printf("\n");
//InOrderVisit(pRoot);
//printf("\n");
PostOrderVisit(pRoot);
printf("\n");
return 0;
}
二叉树的遍历,一般实现方法是用递归,算法逻辑简单直观。而非递归的算法就是手动压栈出栈的步骤来实现的,百度中搜索出来的算法描述理解起来都有些困难,遂花了将近一天时间把非递归算法研究了一遍,并用C代码加以简单实现。
二叉树遍历算法分三种,先序遍历、中序遍历和后序遍历。
1 基本数据结构:
struct TreeNode
{
struct TreeNode* m_pLeftChild;
struct TreeNode* m_pRightChild;
bool m_bStatus; // 是否已经被访问
char m_strData[128];
};
2 简单栈的实现:
struct TreeNode* g_NodeStack[1024];
int g_StackPointer;
void InitStack()
{
for (int i = 0; i < 1024; i++)
{
g_NodeStack[i] = NULL;
}
g_StackPointer = -1;
}
struct TreeNode* StackTop()
{
if(g_StackPointer < 0)
{
printf("\nStack is empty.\n");
return NULL;
}
else
{
return g_NodeStack[g_StackPointer];
}
}
bool PushStack(struct TreeNode* pNode_)
{
if ( (g_StackPointer < 1024 - 1) && (g_StackPointer >=-1) )
{
g_StackPointer++;
g_NodeStack[g_StackPointer] = pNode_;
return true;
}
else
{
printf("\nThe stack memory space is already full.\n");
return false;
}
}
bool PopStack()
{
if ( (g_StackPointer < 1024) && (g_StackPointer >=0) )
{
g_NodeStack[g_StackPointer] = NULL;
g_StackPointer--;
return true;
}
else if(-1>=g_StackPointer)
{
printf("\nThe stack is under range.\n");
return false;
}
else
{
printf("\nThe stack pointer is upper range.\n");
return false;
}
}
3 二叉树初始化: 实现一颗简单二叉树的初始化,仅用于验证算法
void FillNode(struct TreeNode* pNode_, char* strData_)
{
pNode_->m_pLeftChild = NULL;
pNode_->m_pRightChild = NULL;
pNode_->m_bStatus = false;
ZeroMemory(pNode_->m_strData, sizeof(pNode_->m_strData));
copy(strData_, strData_ + strlen(strData_), pNode_->m_strData);
}
void FillNode(struct TreeNode* pNode_, char* strData_,
struct TreeNode* pLeftChild_, struct TreeNode* pRightChild_)
{
pNode_->m_pLeftChild = pLeftChild_;
pNode_->m_pRightChild = pRightChild_;
pNode_->m_bStatus = false;
ZeroMemory(pNode_->m_strData, sizeof(pNode_->m_strData));
copy(strData_, strData_ + strlen(strData_), pNode_->m_strData);
}
struct TreeNode* TreeInit()
{
struct TreeNode* pRoot = (struct TreeNode*)malloc(sizeof(struct TreeNode));
FillNode(pRoot, "A");
struct TreeNode* ppLevel1Node[2] = {NULL, NULL};
for (int i = 0; i < 2; i++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('B' + i);
FillNode(pNode, strData);
ppLevel1Node[i] = pNode;
}
pRoot->m_pLeftChild = ppLevel1Node[0];
pRoot->m_pRightChild = ppLevel1Node[1];
struct TreeNode* ppLevel2Node[4] = {NULL, NULL, NULL, NULL};
int j = 0;
for (j = 0; j < 4; j++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('D' + j);
FillNode(pNode, strData);
ppLevel2Node[j] = pNode;
}
for (j = 0; j < 4; j+=2)
{
ppLevel1Node[j/2]->m_pLeftChild = ppLevel2Node[j];
ppLevel1Node[j/2]->m_pRightChild = ppLevel2Node[j + 1];
}
struct TreeNode* ppLevel3Node[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
int k = 0;
for (k = 0; k < 8; k++)
{
struct TreeNode* pNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
char strData[128] = "";
strData[0] = char('H' + k);
FillNode(pNode, strData);
ppLevel3Node[k] = pNode;
}
for (k = 2; k < 8; k+=2)
{
ppLevel2Node[k/2]->m_pLeftChild = ppLevel3Node[k];
ppLevel2Node[k/2]->m_pRightChild = ppLevel3Node[k + 1];
}
return pRoot;
}
4 先序遍历:
所谓先序遍历,其遍历顺序是当前节点->左子节点->右子节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,并取当前节点为栈顶节点,visit当前节点并标记为已访问节点
(3)弹出栈顶节点
(4)若当前节点有右子节点且右子节点为未访问节点,将右子节点压栈
(5)若当前节点有左子节点且左子节点为未访问节点,将左子节点压栈
(6)跳到第(2)步
(7)结束
代码见示意:
void PreOrderVisit(struct TreeNode* pRoot_)
{
printf("PreOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
// visit
printf("%s ", pNode->m_strData);
pNode->m_bStatus = true;
PopStack();
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PushStack(pNode->m_pRightChild);
}
if (pNode->m_pLeftChild && !pNode->m_pLeftChild->m_bStatus)
{
PushStack(pNode->m_pLeftChild);
}
}
}
5 中序遍历:
中序遍历,其遍历顺序是左子节点->当前节点->右子节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,设当前节点为栈顶节点
(3)若当前节点左子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
(4)若当前节点左子节点不为NULL且左子节点为已访问状态,则visit当前节点并标记为已访问,弹出栈顶节点
(5)若当前节点左子节点不为NULL且左子节点为未访问状态
5.1 若当前节点右子节点不为空,则弹出当前节点,压入右子节点,再压入当前节点
5.2 压入左子节点
(6)跳到第(2)步
(7)结束
代码见示意:
void InOrderVisit(struct TreeNode* pRoot_)
{
printf("InOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
if (!pNode->m_pLeftChild)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if (pNode->m_pLeftChild->m_bStatus)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else
{
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PopStack();
PushStack(pNode->m_pRightChild);
PushStack(pNode);
}
PushStack(pNode->m_pLeftChild);
}
}
}
6 后序遍历
后序遍历,其遍历顺序是左子节点->右子节点->当前节点
基本算法:
(1)Root节点压栈
(2)若栈为空,则跳转到第(7)步;
若栈不为空,取栈顶节点,设当前节点为栈顶节点
(3)对当前节点作判定处理:
3.1 若左子节点为NULL且右子节点为NULL,则则visit当前节点并标记为已访问,弹出栈顶节点
3.2 若左子节点不为NULL且已被访问 且右子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
3.3 若右子节点不为NULL且已被访问 且左子节点为NULL,则visit当前节点并标记为已访问,弹出栈顶节点
3.4 若左子节点不为NULL且已被访问 且 右子节点不为NULL且已被访问在,则visit当前节点并标记为已访问,弹出栈顶节点
(4)若右子节点不为NULL且右子节点未被访问,右子节点入栈
(5)若左子节点不为NULL且左子节点未被访问,左子节点入栈
(6)跳到第(2)步
(7)结束
代码见示意:
void PostOrderVisit(struct TreeNode* pRoot_)
{
printf("PostOrderVisit: \n");
if (!pRoot_)
{
return;
}
InitStack();
PushStack(pRoot_);
struct TreeNode* pNode = NULL;
while(StackTop())
{
pNode = StackTop();
if (!pNode->m_pLeftChild && !pNode->m_pRightChild)
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if( !pNode->m_pRightChild && (pNode->m_pLeftChild && pNode->m_pLeftChild->m_bStatus) )
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if( !pNode->m_pLeftChild && (pNode->m_pRightChild && pNode->m_pRightChild->m_bStatus) )
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
else if ( (pNode->m_pRightChild && pNode->m_pRightChild->m_bStatus)
&& (pNode->m_pLeftChild && pNode->m_pLeftChild->m_bStatus))
{
pNode->m_bStatus = true;
printf("%s ", pNode->m_strData);
PopStack();
}
if (pNode->m_pRightChild && !pNode->m_pRightChild->m_bStatus)
{
PushStack(pNode->m_pRightChild);
}
if (pNode->m_pLeftChild && !pNode->m_pLeftChild->m_bStatus)
{
PushStack(pNode->m_pLeftChild);
}
}
}
补充测试的main函数:
#include <stdio.h>
#include <windows.h>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[])
{
struct TreeNode* pRoot = NULL;
pRoot = TreeInit();
//PreOrderVisit(pRoot);
//printf("\n");
//InOrderVisit(pRoot);
//printf("\n");
PostOrderVisit(pRoot);
printf("\n");
return 0;
}