二叉树的遍历(递归、非递归)

来源:互联网 发布:乌玛·瑟曼 知乎 编辑:程序博客网 时间:2024/06/16 11:10

二叉树遍历的基本概念

1、二叉树遍历原理:二叉树的遍历是指从根结点出发,按照某种次序依次访问所有结点,使得每个结点被访问一次且仅被访问一次。
2、二叉树遍历方法:
(1)前序遍历:若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
(2)中序遍历:若树为空,则空操作返回,否则从根结点开始,中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
(3)后序遍历:若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
(4)层序遍历:若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。

二叉树遍历举例:

这里写图片描述
二叉树的前序遍历序列: ABCDEFGH
二叉树的中序遍历序列:CBEDFAGH
二叉树的后续遍历序列:CEFDBHGA
二叉树的层序遍历举例:ABGCDHEF

二叉树遍历算法

1、前序遍历算法
(1)递归算法

void PreOrder(BtNode *ptr){    if (NULL != ptr)    {        printf("%c ",ptr->data);        PreOrder(ptr->leftChild);        PreOrder(ptr->rightChild);    }}

(2非递归算法
思路1:
对于任一结点pCur:
1)访问结点pCur,并将结点pCur入栈,结点pCur一直左走 ;
2)判断结点pCur的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点pCur,循环至 1);若不为空,则将P的左孩子置为当前的结点pCur;
3)直到pCur为NULL并且栈为空,则遍历结束。

void NicePreOrder(BtNode *ptr){    if (ptr == NULL)    {        return;    }    stack<BtNode *> stackTreeNodes;     BtNode *pCur = ptr;//指向当前要访问的结点      while(pCur != NULL || !stackTreeNodes.empty())      {          while(pCur != NULL)//一直往左走          {              printf("%c ",pCur->data);              stackTreeNodes.push(pCur);              pCur = pCur->leftChild;          }        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空          {              pCur = stackTreeNodes.top();//pCur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点              stackTreeNodes.pop();              pCur = pCur->rightChild;          }      }  }

思路2:
1)先判断树是否为空,若为空则返回空操作,否则将根结点入栈;
2)然后判断栈为不为空,不为空则访问栈顶元素并弹出栈顶元素;
3)判断右子树是否为空,不为空则将右子树根结点压入栈,再判断左子树是否为空,不为空则将左子树根结点压入栈,继续进行2)操作,直到栈为空为止。

void NicePerOrder(BtNode *ptr){    if(NULL == ptr)     {        return ;    }    Stack st;    Init_Stack(&st);    push(&st,ptr);    while(!empty(&st))    {        ptr = top(&st);         pop(&st);        printf("%c ",ptr->data);        if(ptr->rightchild != NULL)            push(&st,ptr->rightchild);        if(ptr->leftchild != NULL)            push(&st,ptr->leftchild);    }}

2、中序遍历算法
(1)递归算法

void InOrder(BtNode *ptr){    if (ptr == NULL)    {        return;    }    InOrder(ptr->leftChild);    printf("%c ",ptr->data);    InOrder(ptr->rightChild);}

(2)非递归算法
思路1:
对于任一结点pCur:
1)将结点pCur入栈,结点pCur一直左走
2)判断结点pCur的左孩子是否为空,若为空,访问结点pCur,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点pCur,循环至 1);若不为空,则将P的左孩子置为当前的结点pCur;
3)直到pCur为NULL并且栈为空,则遍历结束。

void NiceInOrder(BtNode *ptr){    if (ptr == NULL)    {        return;    }    stack<BtNode *> stackTreeNodes;    BtNode *pCur = ptr;//指向当前子树的根结点      while(pCur != NULL || !stackTreeNodes.empty())      {          while(pCur != NULL)//一直往左走          {              stackTreeNodes.push(pCur);              pCur = pCur->leftChild;          }        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空          {              pCur = stackTreeNodes.top();//pCur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点              printf("%c ",pCur->data);            stackTreeNodes.pop();              pCur = pCur->rightChild;          }      }  }

3、后序遍历算法
(1)递归算法

void PostOrder(BtNode *ptr){    if (ptr != NULL)    {        PostOrder(ptr->leftChild);        PostOrder(ptr->rightChild);        printf("%c ",ptr->data);    }}

(2)非递归算法
思路1:
1)设立两个指针pCur (初始指向根结点),和preVisted(指向被访问结点),对于任一结点pCur,将其入栈;
2)然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,此时要判断此结点是否满足输出条件,即:当前结点的右孩子如果为空或者已经被访问,则访问当前结点 ,否则将pCur置为其右结点,直到pCur为NULL并且栈为空,则遍历结束。

void NicePostOrder(BtNode *ptr){    if(ptr == NULL)    {          return;      }      stack<BtNode *> stackTreeNodes;      BtNode *pCur = ptr;//指向当前子树的根结点      BtNode *preVisted = NULL;//指向前一被访问的结点      while(pCur != NULL || !stackTreeNodes.empty())      {          while(pCur != NULL)//一直向左走          {              stackTreeNodes.push(pCur);              pCur = pCur->leftChild;          }        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空          {              //pCur刚跳出上面的while循环时值为NULL,故要将pCur指向当前的栈顶结点              pCur = stackTreeNodes.top();            if(pCur->rightChild == NULL || pCur->rightChild==preVisted)//当前节点的右孩子如果为空或者已经被访问,则访问当前节点              {                  printf("%c ",pCur->data);                  stackTreeNodes.pop();                  preVisted = pCur;//设置当前结点已被访问过                pCur = NULL;//防止当前结点又被压入栈中             }            else//如果当前结点的右子结点存在且没被访问过              {                  pCur = pCur->rightChild;              }          }      }  }

思路2:
1)设立标记结点指针,以此来判断右子树是否已被访问过,先将根结点入栈;
2)然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,将该结点出栈,并判断该结点的右孩子是否为空或者已经被访问(即等于标记结点指针),若是则访问该结点 ,并将该结点指针赋值给标记结点,该结点指针置为空,否则将该结点重新压入栈顶,直到当前结点指针和栈都为空,则遍历结束。

void NicePastOrder(BtNode *ptr){    if(ptr == NULL) return ;    Stack st;    Init_Stack(&st);    BtNode *tag = NULL;    while(ptr != NULL || !empty(&st))    {        while(ptr != NULL)        {            push(&st,ptr);            ptr = ptr->leftchild;        }        ptr = top(&st); pop(&st);        if(ptr->rightchild == NULL || ptr->rightchild == tag)        {            printf("%c ",ptr->data);            tag = ptr;            ptr = NULL;        }        else        {            push(&st,ptr);            ptr = ptr->rightchild;        }    }}

4、层序遍历算法
思路:
1)先判断树是否为空,不为空则将根结点入队列;
2)然后判断队列是否为空,不为空则出队头元素,并访问此结点;
3)先判断当前结点左子树是否为空,不为空则将左孩子结点入队列,再判断当前结点的右子树是否为空,不为空则将右孩子结点入队列,继续进行2)操作,直到队列为空为止。

void LevelOrder(BtNode *ptr){    assert(ptr != NULL);    queue<BtNode *> queueTreeNodes;      queueTreeNodes.push(ptr);    while(!queueTreeNodes.empty())      {          BtNode *pNode=queueTreeNodes.front();          queueTreeNodes.pop();        printf("%c ",pNode->data);        if(pNode->leftChild!=NULL)        {              queueTreeNodes.push(pNode->leftChild);        }          if(pNode->rightChild!=NULL)        {              queueTreeNodes.push(pNode->rightChild);          }      }  }