Sszl

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

数与二叉树(二)二叉树的遍历

一、二叉树的结构

typedef struct BTNode{

char data; //默认data域为char,可修改

struct BTNode  *lchild;

struct BTNode  *rchild;

}BTNode;

 

二、递归遍历

1.先序遍历

算法思想:

1)访问根节点

2)  访问左子树

3)访问右子树

代码实现:

void preorder(BTNode p){

  if(p!=null){

    visit(p);

    preorder(p->lchild);

    preorder(p->rchild);

    }

  }

2.中序遍历

算法思想:

1)访问左子树

2)  访问根节点

3)访问右子树

代码实现:

void inorder(BTNode p){

  f(p!=null){

    preorder(p->lchild);

    visit(p);

    preorder(p->rchild);

    }

  }

3.后序遍历

算法思想:

1)访问左子树

2)  访问右子树

3)访问根节点

代码实现:

void postorder(BTNode p){

  if(p!=null){

    preorder(p->lchild);

    preorder(p->rchild);

    visit(p);

    }

 }

 

三、非递归遍历

1、先序遍历

算法思想:

用堆栈储存结点,如果根节点不为空,根结点作为元素进栈。当栈不为空时进行循环,首先,出栈一个结点并访问,如果当前结点有右子树,则右子树进栈;如果有左子树,则左子树进栈。继续循环。(利用了栈的后进先出原则,因为要先访问左孩子所以右孩子先进栈)

代码实现:

void preorderNR(BTNode p){

  if (p!=null){

    stack=(*BTNode)malloc(sizeof(BTNode));//定义一个栈

    int top = -1;//初始化栈

    stack[++top]=p;

      while(top != -1){

      p=stack[top--];//下一个结点出栈

      visit(p);

      if(p->rchild!=null) 

          stack[++top]=p->rchild;//若右子树存在,则进栈

      if(p->lchild!=null)

          stack[++top]=p->lchild;//若左子树存在,则进栈

      }

    }

  }

2、中序遍历

算法思想:

用堆栈储存结点,当当前结点不为空或者栈不为空时,进入循环,如果当前结点不为空则当前结点进栈,且当前结点指向当前结点的左孩子,直到当前结点为空,如此栈顶元素便是最左边的结点。当前结点出栈,访问之,当前结点的指针指向其右孩子,继续循环。

代码实现:

void inorderNR(BTNode p){

  stack=(*BTNode)malloc(sizeof(BTNode));//定义一个栈

  int top = -1;//初始化栈

  while (p!=null || top!=-1){

 

    while(p!=null){

    stack[++top]=p;

    }//寻找最左边的结点

  p=stack[top--];

  visit(p);

  p=p->rchild;//右子树进栈

  }

}

此算法无需判断右结点是否为空,因为如果右结点为空,栈顶结点便会直接出栈。

3、后序遍历

(1)算法一

算法思想:

后序遍历的非递归算法实现比较复杂。因为我们找到遍历左子树时,根节点及其左子树的根节点要进栈,当遍历完左子树,我们要让其根节点出栈,找他的右子树,这个过程根节点及其右子树的根节点又要进栈,遍历完右子树,根节点出栈并访问。由于每个非终端结点都经历了两次出栈,而第二次出栈我们才可以访问,所以我们引入一个同步栈,第一次进栈时,同步进入元素0,第二次进栈时,同步进入元素1,出栈时,如果同步栈出栈元素为1就访问。

代码实现:

void postorderNR(BTNode p){

  BTNode *stack1[maxsize];    //定义一个栈用于储存结点

  int *stack2[maxsize];          //定义一个同步栈用于判断是否访问输出结点

  int top = -1;//初始化两个栈

  int tag;//用于同步栈输出

  while(p!=null || top!=-1){ 

    while(p!=null){ stack1[++top]=p;

      stack2[++top]=0;

      p=p->lchild;}       //寻找最左边的结点

  p=stack1[top];

  tag=stack2[top--];

  if(tag==0){

    while(p!=null){ stack1[++top]=p;

      stack2[++top]=1;

      p=p->rchild;}

    }//寻找该子树下最右边的结点

  else if(tag==1)

    {visit(p);

    p=null;//避免死循环

    }

  }

}

(2)算法二

算法思想:

比较二叉树的先序遍历序列和后序遍历序列,不难发现,后序遍历的逆序列,就是先序遍历改变访问左右子树的顺序得到的序列。即后序遍历的逆序列就是先访问结点,再访问右子树,再访问左子树得到的序列。那么我们将如此访问得到的序列压入栈中后,输出栈中的全部元素,即可得到后序遍历的序列。

算法实现:

postorderNR(BTNode p){

  if(p !=null ){

  BTNode *stack1[maxsize];     //定义一个栈用于储存结点

  BTNode *stack2[maxsize]     //定义一个同步栈用于判断是否访问输出结点

  int top1 = -1;//初始化栈1

  int top2 = -1;//初始化栈2

  stack1[++top1]=p;

  while(top1!=-1){

    p = stack[top1--];

    stack[++top2]=p;

    if(p->lchild != null)

      stack1[++top1]=p->lchild;

    if(p->rchild != null)

      stack1[++top1]=p->rchild;

    }

  while(top2 != -1){

    p=stack[top2--];

    visit(p);

    }

  }

}

四、层次遍历

算法思想:层次遍历要求自上而下、自左而右访问二叉树。除根结点外,每一层的结点都是上一层结点的孩子结点。利用队列先进先出的性质,每个结点出队时,他的子结点就进入队列,队列为空则循环结束。

代码实现:

void level(BTNode p){

  if(p!=null){

    que=(*BTNode)malloc(sizeof(BTNode));

    int front, rear;

    front=0;

    rear=front;

    que(++rear)=p;

    while(rear>front){

      p=que(front--);

      visit(p);

      if(p->lchild != null) 

        que[++rear]=p.lchild;

      if(p->rchild != null) 

        que[++rear]=p.rchild;

      }

    }

}

 

posted on 2018-09-08 02:42  Sszl  阅读(275)  评论(0)    收藏  举报