先序遍历和后序遍历为什么不能唯一地确定一棵树?

以前大学学数据结果的时候,我们就知道,根据一棵树的先序遍历和中序遍历,或者后序遍历和中序遍历序列,都可以唯一地确定一棵树。

树中的节点,分为度为0,1,2的结点。如果树中只有一个节点,那么可以唯一确定一棵树,即只有一个节点的树。

当树中结点个数大于等于2的情况,树中的叶子结点和它的父亲结点中,至少有一种存在如下的情况。(为方便起见,我们先从叶子节点入手)

             case 1:                             case2:                      case 3:

                 A                                    D                                   F

              /     \                                 /                                        \

           B       C                            E                                          G


即,叶子结点的父亲有两个孩子,只有左孩子,只有右孩子的情况。我们只需要证明,如果树存在这三种结构中的哪一种,可以唯一确定一棵树,什么情况下又不能唯一确定一棵树呢?

1.       case 1:   

                 A       

              /     \     

           B       C        

前序遍历:  ABC, 后序遍历: BCA

现在,我们根据遍历序列,看看能否得到另一种树的结构?

由于在前序遍历中A第一个出现,则A为这棵树的根节点。(前: A  BC        后: BC A)

接下来,我们看,BC可以只在A的左子树或者右子树中出现吗?

如果BC只出现在树的左子树或者右子树中,则根据前序遍历, B应为子树的树,C为B的孩子。则后序遍历时,C应在B的前面。但实际的后序遍历,C在B的后面。因此,BC不可能只出现在A的左子树或者右子树当中。因此,在这种情况下,可以唯一确定树的结构。


2.     case 2:

                        D

                      /

                   E

前序遍历: DE, 后序遍历: ED

则下面树的结构也满足前序和后序遍历的序列。

                        D

                          \

                            E

即这种情况,不能唯一确定一棵树。

3.       case 3:  

同case 2情况相似,也不能唯一确定一棵树。


我们可以把叶子结点推广成一棵树的情况,即如果树中只存在度为0和度为2的节点,则根据它的前序遍历和后序遍历序列,可以重构树的结构。否则不能唯一重构树。那如果给你一个前序和后序遍历的序列,我们如何来判断,它是否可以唯一地构造一棵树呢?

即我们根据遍历序列,来看看树中,如果所有结点的度为0或者2,则可以唯一还原。

例: 前序: ABDFGEC  后序: FGDEBCA

第一步: 根结点为A 

第二步: 根据前后序序列,B为A的左子树的根,根据后序序列 将整个序列分为两部分: FGDEB, C 即A有两个孩子。

第三步: 继续看以B为树的树,前序为 BDFGE, 后序: FGDEB 。 按照分析A的方法来分析 B,最后得知,这棵树可以唯一确定。


但是如果把上面序列中的结点C给去掉,即:前序: ABDFGE  后序: FGDEBA, 此时就不能唯一确定了。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 递归先序遍历: 1. 访问根节点 2. 递归遍历左子 3. 递归遍历右子 非递归先序遍历: 1. 将根节点入栈 2. 当栈不为空时,弹出栈顶元素并访问 3. 如果该节点有右子,将右子入栈 4. 如果该节点有左子,将左子入栈 5. 重复步骤2-4,直到栈为空 递归后序遍历: 1. 递归遍历左子 2. 递归遍历右子 3. 访问根节点 非递归后序遍历: 1. 将根节点入栈 2. 初始化一个辅助栈 3. 当栈不为空时,弹出栈顶元素并压入辅助栈 4. 如果该节点有左子,将左子入栈 5. 如果该节点有右子,将右子入栈 6. 重复步骤3-5,直到栈为空 7. 依次弹出辅助栈中的元素并访问,即为后序遍历结果 ### 回答2: 二叉遍历是指按照某种次序依次访问二叉中的所有结点,二叉遍历有三种方式:先序遍历、中序遍历后序遍历。其中,先序遍历是指先访问根节点,再递归遍历左子和右子;中序遍历是指先递归遍历左子,再访问根节点,最后递归遍历右子后序遍历是指先递归遍历左子和右子,最后访问根节点。 非递归先序遍历二叉的方法是使用栈来进行遍历。首先将根节点入栈,然后进入循环,每次取出栈顶元素,访问该结点,然后先将右子入栈(如果有的话),再将左子入栈(如果有的话)。重复执行直到栈为空。 非递归后序遍历二叉的方法也是使用栈来进行遍历。先将根结点入栈,然后进入循环,每次取出栈顶元素,如果该元素的左右子均为空或者访问过了,则访问该元素。否则,将该元素的左右子入栈(右子先入栈),继续循环。需要在遍历结束时清空遍历标志,以便下一次循环。 递归实现方法也很简单,对于先序遍历,我们只需要按照先访问根节点、再递归遍历左子和右子的顺序进行遍历即可;对于后序遍历,我们只需要按照先递归遍历左子和右子,最后访问根节点的顺序进行遍历即可。 总之,在实际编程中应该根据实际情况选择使用哪种方法,对于非常大的二叉,递归可能会导致堆栈溢出的问题,而非递归遍历则可以避免这个问题。 ### 回答3: 二叉是一种重要的数据结构,不同的二叉遍历方法可以高效的访问节点或者。在二叉遍历中,先序遍历后序遍历是两种常见的方式。我们可以采用递归和非递归的方式来实现这两个遍历方法。 一、递归方式实现二叉先序遍历后序遍历 1. 先序遍历 先序遍历遍历顺序是“根节点 -> 左子 -> 右子”,我们可以采用递归的方式进行遍历。 具体实现方式如下: def preorderTraversal(root): if not root: return print(root.val) preorderTraversal(root.left) preorderTraversal(root.right) 2. 后序遍历 后序遍历遍历顺序是“左子 -> 右子 -> 根节点”,同样采用递归的方式遍历。 具体实现方式如下: def postorderTraversal(root): if not root: return postorderTraversal(root.left) postorderTraversal(root.right) print(root.val) 二、非递归方式实现二叉先序遍历后序遍历 1. 先序遍历 首先,我们需要借助栈来实现非递归的先序遍历。具体实现方式如下: def preorderTraversal(root): if not root: return [] stack, res = [root], [] while stack: node = stack.pop() if node: res.append(node.val) if node.right: stack.append(node.right) if node.left: stack.append(node.left) return res 2. 后序遍历 非递归的后序遍历需要维护两个栈,具体实现方式如下: def postorderTraversal(root): if not root: return [] stack1, stack2, res = [root], [], [] while stack1: node = stack1.pop() if node: stack2.append(node) if node.left: stack1.append(node.left) if node.right: stack1.append(node.right) while stack2: res.append(stack2.pop().val) return res 总之,二叉遍历是非常常见的算法问题,其中先序遍历后序遍历是两种重要的方式。通过递归和非递归两种实现方式可以使得程序更加高效,更加灵活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值