二叉树非递归遍历之简单实现

来源:互联网 发布:中国产业信息网数据库 编辑:程序博客网 时间:2024/05/22 01:45
二叉树作为一个常见数据结构,就不细说其具体结构了(不知道先面壁去:)),在实际工作中很常见,在面试过程中也会经常被涉及到,一些排序算法的基础也是二叉树。
二叉树的遍历,一般实现方法是用递归,算法逻辑简单直观。而非递归的算法就是手动压栈出栈的步骤来实现的,百度中搜索出来的算法描述理解起来都有些困难,遂花了将近一天时间把非递归算法研究了一遍,并用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;
}
0 0
原创粉丝点击