vlambda博客
学习文章列表

数据结构—【二叉树(深度优先遍历)】重难点



知识总结

关注我


01
重难点

"

重点:

  1. 使用栈的遍历算法思想

  2. 非递归算法的实现


难点:

  1. 非递归算法的实现及栈的思想

"


02
遍历算法思想
01
算法思想


在上一章我们讲了三种遍历算法,但有没有同学想过为什么要这样,只知道三种遍历的顺序,没有学会算法的核心思想
遍历树的核心就是咱们之前学的栈,不信来一起看看

数据结构—【二叉树(深度优先遍历)】重难点

这个图还是上一章的,以中序遍历为例,我们对照着讲。
中序遍历的结果是:4,2,5,1,3
过程是:
1.  树不空,1的左孩子为2将1进栈,压入栈底,中序遍历左子树(2,4,5)。

数据结构—【二叉树(深度优先遍历)】重难点


2.  2不空,2的左孩子为4将2进栈,再中序遍历左子树(4)。

数据结构—【二叉树(深度优先遍历)】重难点


3.  4没有左孩子了,访问4,将4进栈,下面不能进了。

数据结构—【二叉树(深度优先遍历)】重难点


4. 4没有左孩子了,无法进栈,这时开始出栈,先将4出栈,4的根节点为2,回到根节点,访问2。

数据结构—【二叉树(深度优先遍历)】重难点


5.  2已经入栈了,无法入栈,再出栈,将2出栈,2的右孩子为5,回到右孩子,访问5.

数据结构—【二叉树(深度优先遍历)】重难点


6.  5没有入栈,将5入栈,访问根节点1。


7.  1已经入栈,无法入栈,将5出栈。


8.  将1出栈,中序遍历右子树(3),访问3,将3进栈,再出栈,栈中元素为空,遍历结束。

03
非递归遍历算法实现
02
深度优先遍历


1.  先序遍历

void preorderNonrecursion(BTNode *bt){ if(bt != NULL) { // 定义一个栈 BTNode *Stack[MaxSize]; // 指针设为-1,第一个元素在0处 int top = -1; // 再设一个结点p BTNode *p; // 将根节点入栈 Stack[++top] = bt; // 孩子不空则进行入栈和出栈 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 inorderNonrecursion(BTNode *bt){ if(bt != NULL) { // 定义一个栈 BTNode *Stack[MaxSize]; // 指针设为-1,第一个元素在0处 int top = -1; // 再设一个结点p      BTNode *p;      p = bt;       while(top!=-1 || p!=NULL) {         // 找到最左孩子,并将左一侧孩子都入栈         while(p!=NULL)         {            Stack[++top] = p;            p = p -> lchild;         }                  if(top!=-1)         {            p = Stack[top--];            Visit(p);            p = p ->rchild;         } } }}

3.  后序遍历

void postorderNonrecursion(BTNode *bt){ if(bt != NULL) {      // 定义两个栈 BTNode *Stack1[MaxSize];      BTNode *Stack2[MaxSize]; // 指针设为-1,第一个元素在0处 int top1 = -1;      int top2 = -1; // 再设一个结点p      BTNode *p = NULL; // 将根节点入栈 Stack[++top1] = bt; // 孩子不空则进行入栈和出栈 while(top1 !=-1) {         // 这里使用先序遍历入栈1 p = Stack1[top1--];         // 紧接着入栈2         Stack2[++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);      } }}
这里的后序遍历较难理解,我们来详细讲解一下:
通过观察上面例子的先序和后序遍历的结果顺序,
先序为:1( 根节点1),((2) 根节点2,(4)左子树1,(5)右子树2)左子树1,(3)右子树1
            12,4,5,3
后序为:4,5, 2,3, 1
其中1,2,为两个根节点,(2,4,5),(3)为左右子树,根据先序和后序遍历的顺序,后序遍历相当于把根节点的左右子树位置互换1,3,2,5,4,(其中1为根节点,2,4,5和3互换得到1,3,2,4,5,再以2为根节点,将4和5互换得到1,3,2,5,4,然后再将整体逆序得到4,5,2,3,1。
实现思想是:用两个栈,第一个栈实现先序遍历,然后第二个栈接受先序遍历的左右子树互换结果(出栈1再入栈2就实现了)再全出栈2就实现全部的逆序,得到后序遍历结果。一次互换左节点变成右节点,二次逆序又变回来了,但内部的顺序交换了。


分享 收藏 点赞 在看

END

往期回顾: