二叉树基础
关于二叉树的基础结构,请参考
《剑指offer》预备知识-链表与二叉树
今天我们以如下这棵二叉树作为例子。
重构二叉树
offer06的要求是,给出一棵二叉树的前序遍历和中序遍历,要求重构这棵二叉树,那我们就需要知道什么叫前序遍历和中序遍历(顺便说说后续遍历)。
前序遍历
对于每一个结点按照根结点→左子结点→右子结点的方式遍历。
在本树中,先考察根结点6,它有左结点3和右结点7
对于左结点3,它作为根结点的话,有左结点1和右结点5
对于左结点1,它作为根结点的话,有右结点2作为叶结点。所以有6→3→1→2
回到左结点3的右结点5,它作为根结点的话有左结点4,所以有5→4
所以6的左子树的前序遍历为6→3→1→2→5→4
同理右子树的遍历为7→9→8→10,整个二叉树的前序遍历为6→3→1→2→5→4→7→9→8→10
中序遍历
对于每一个结点,按照左子结点→根结点→右子结点的方式遍历。
在本树中,先考察根结点6,它有左结点3,
对于左结点3,它有左结点1
对于左结点1,它没有左子树了,左→根→右,它有右结点2作为叶结点,所以1→2
回到左结点3,左子树遍历完成,考察右结点5,它有左结点4,因此有4→5,中间以3连接,即:1→2→3→4→5
回到根结点6,左子树遍历完成,同理遍历右子树,有7→8→9→10,最终合在一起就是:
1→2→3→4→5→6→7→8→9→10
后序遍历
对于每一个结点,按照左子结点→右子结点→根结点的方式遍历。
在本树中,先考察根结点6,它有左结点3
对于左结点3,它有左结点1,
对于左结点1,它有右结点2作为叶结点所以有2→1
回到左结点3,左子树遍历完成,考察右结点5,它有左结点4,因此有4→5,连在一起,最后才遍历3,这里是2→1→4→5→3,
回到根结点6,左子树遍历完成,同理遍历右子树:8→10→9→7,最后遍历根结点6,合在一起是:
2→1→4→5→3→8→10→9→7→6
从前中序遍历中找规律
现在我们不看图,我们知道一棵二叉树的前序遍历:
6→3→1→2→5→4→7→9→8→10
中序遍历:
1→2→3→4→5→6→7→8→9→10
如何人肉地把二叉树构建出来呢?
我们知道前序遍历按照根结点→左子结点→右子结点的方式遍历,因此前序遍历的第一个结点:6就是根结点。而中序遍历按照左子结点→根结点→右子结点的方式遍历,因此中序遍历中,6左边的是6的左子树,6右边的是6的右子树。
继续看前序遍历的3,它是6的左子树的根结点,和上述同理,我们从中序遍历中可以得知,1和2是3的左子树,4和5是3的右子树。
前序遍历和中序遍历都是1→2,意味着2是1的右结点。与此相反的,前序遍历给出了5→4,中序遍历给出了4→5,意味着4是5的左结点。至此根结点6的左子树重构完成。
那么右子树也是同理了,容易得到9是7的右结点,8和10是9的左右结点。
代码
回顾整个过程——先序遍历的第一个结点肯定是根结点,然后在中序遍历中找到此根结点,结点以左是二叉树的左子树,根结点以右是二叉树的右子树。然后,左右子女树又可以单独的看成一个独立的二叉树,然后再对其划分,如此递归
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
# offer06-solution
def reConstructBinaryTree(self, pre, tin):
if not pre or not tin:
return None
root = TreeNode(pre[0])
val = tin.index(pre[0])
root.left = self.reConstructBinaryTree(pre[1:val + 1], tin[:val])
root.right = self.reConstructBinaryTree(pre[val + 1:], tin[val + 1:])
return root
二叉树的下一个结点
offer07给出了一棵二叉树,要求它某个结点的中序遍历中的下一个结点。还是以刚才的二叉树为例:
1→2→3→4→5→6→7→8→9→10;要求给1输出2,给5输出6
由前面的知识,我们分情况讨论:
1、如果这个结点有右子树,那么下一个结点为右子树的最左结点(递归/while)
比如结点6,它的下一个结点7没有左子树,所以6的下一个结点就是7
而结点7,它的下一个结点9有左结点8,左结点8是叶结点,所以7的下一个结点是8
2、如果这个结点没有右子树,那么
2.1、如果这个结点是它的父结点的左孩子,那么该结点的下一个结点就是其父结点
比如4和8
2.2、如果这个结点是它的父结点的右孩子,那么就要一直向上追溯父结点的父结点,的父结点……直到某个父结点是其父结点的左孩子。
比如5,就要向上回溯两层到6.
根据这个规律,题解也呼之欲出了。
class TreeLinkNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
self.next = None # next是父结点
# offer07-solution
def GetNext(self, pNode):
if not pNode:
return
# 如果该结点有右子树,那么下一个结点就是它右子树中的最左结点
elif pNode.right != None:
pNode = pNode.right
while pNode.left != None:
pNode = pNode.left
return pNode
# 如果一个结点没有右子树,并且它还是它父结点的右子结点
elif pNode.next != None and pNode.next.right == pNode:
while pNode.next != None and pNode.next.left != pNode:
pNode = pNode.next
return pNode.next
# 如果一个结点是它父结点的左子结点,那么直接返回它的父结点
else:
return pNode.next
小结
这两题主要考察三序遍历的基础知识是否扎实。
参考:
二叉树的下一个结点(思路与实现)