最近发现python写OJ很方便,试了一下树的遍历。
树的结构:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
前序遍历:
递归的方法:(这个递归在自己电脑上测试是没问题的,网友测试也没问题,不知为啥leetcode不通过)
class Solution:
# @param root, a tree node
# @return a list of integers
def preorderTraversal(self, root,result=[]):
if root==None:
return result
else:
result.append(root.val)
Solution.preorderTraversal(self,root.left,result)
Solution.preorderTraversal(self,root.right,result)
return result
递归其实没什么好讲的,就是套用递归的一个模型:
void func( mode){
if(endCondition)
{
constExpression //基本项
}
else
{
accumrateExpreesion //归纳项
mode=expression //步进表达式
func(mode) / /调用本身,递归
}
}
具体的按照需求往里套一套,树的遍历的递归还是很好理解的,前序遍历就是按照前序遍历的概念来的,先访问根节点,如果左子树不为空,则前序遍历左子树,否则前序遍历右子树。
下面具体说一说非递归的前序遍历,也就是题目要求的方法,先上代码。
代码1
class Solution:
# @param root, a tree node
# @return a list of integers
def preorderTraversal(self, root):
stack=[] #模拟栈的list
result=[] #存放结果的list
if root!=None:
stack.append(root)
while len(stack)!=0:
length=len(stack)
root=stack.pop(length-1) #弹出栈顶元素
result.append(root.val) #访问栈顶元素
if root.right!=None: #右叶子入栈
stack.append(root.right)
if root.left!=None: #左叶子入栈
stack.append(root.left)
return resultpython的list比较强大,队列和栈都能模仿,这里我们用来模仿栈。进栈就是简单的append,出栈的话,我们指定pop最后一个元素,因为最后一个元素是最近才加入的,先进后出嘛。
前序遍历的访问顺序是根节点->左叶子->右叶子,此方法的栈其实就是模拟递归的,将根节点先存入栈,然后弹出访问,分别把它的右叶子和左叶子压入栈(因为先左再右)。下次循环弹出的就是左叶子,左叶子就是左子树的根节点,访问它,再把它的右叶子和左叶子压入栈,依次往复,达到了前序遍历左子树的目的。当左子树遍历完了,下一个弹出的就是根节点的右叶子,同理再前序遍历右子树,完成遍历。
代码2
class Solution:
# @param root, a tree node
# @return a list of integers
def preorderTraversal(self, root):
result=[]
stack=[]
while (root!=None or len(stack)!=0):
if(root!=None):
result.append(root.val) #当前节点不为空就访问它
stack.append(root) #访问后再入栈
root=root.left #访问下一个节点
else:
length=len(stack)
root=stack.pop(length-1) #弹出栈顶元素
root=root.right #通过栈顶元素找到右子树
return result与上一种方法,上来就访问根节点(相对的,也包括子树的根节点),然后把其右、左子树都压入栈不同(这之后根节点实际就没有作用,被我们舍弃了),第二种方法与直观思维比较相符,前序遍历无非就是我从根节点开始,有左叶子就往左走,一直找到最左的那一个。由于是先访问根节点,所以我们把路过的根节点都给访问了,但是这里我们却不能把它舍弃了,因为到目前为止,我们并没有处理右叶子,所以我们在访问完根节点之后把它压入栈,用于之后找到对应的右叶子节点。这就是第二种方法的思路了,如果“根节点”还有左叶子就访问根节点,根节点入栈,指向左叶子,如果没有了,就再通过栈中的“根节点”找到右叶子继续。
中序遍历:
递归算法:
class Solution:
# @param root, a tree node
# @return a list of integers
def inorderTraversal(self, root,result=[]):
if root==None:
return result
else:
Solution.inorderTraversal(self,root.left,result)
result.append(root.val)
Solution.inorderTraversal(self,root.right,result)
return result
递归算法具体就不说了,还是按照定义来的。
非递归算法:
代码3
class Solution:
# @param root, a tree node
# @return a list of integers
def inorderTraversal(self, root):
stack=[]
result=[]
while (root!=None or len(stack)!=0):
if root!=None:
stack.append(root) #根节点入栈,并不访问
root=root.left #找到左叶子
else:
length=len(stack)
root=stack.pop(length-1) #根节点出栈
result.append(root.val) #访问根节点
root=root.right #找到右叶子
return result因为中序遍历的顺序是:左叶子->根节点->右叶子,根节点像代码2那样要求是不丢弃的,所以我们首先想到的就是模仿代码2来操作,思路和代码2基本是一样的,只是将访问根节点移到了弹出根节点之后、找到右叶子之前。(之前是先访问再入栈,现在是先入栈,再出栈在访问)代码4
class Solution:
# @param root, a tree node
# @return a list of integers
def inorderTraversal(self, root):
stack=[]
result=[]
if root!=None:
stack.append(root)
root.be=False #对节点增加了访问标志位(局部类属性)
while len(stack)!=0:
root=stack.pop(len(stack)-1)
if root.be==True:
result.append(root.val) #如果右、左叶子已入栈,则访问
else:
if root.right!=None: #右叶子入栈,右叶子标志位设为False
stack.append(root.right)
root.right.be=False
stack.append(root) #根节点入栈
if root.left!=None: #左叶子入栈,左叶子标志位设为False
stack.append(root.left)
root.left.be=False
root.be=True #右、左叶子都已入栈,则根节点的标志位设为True
return result再来想一想代码1的方法,前序遍历由于根节点先访问,因此访问之后可以丢掉不管了,但是中序遍历却不可以,因为要先访问左叶子再访问根节点。也就是说在左叶子入栈之前,根节点也要入栈,但是如果根节点出栈再入栈出栈再入栈,那就是个死循环了,所以这里必须引进一个标志位,来确定“根节点”的右、左叶子是不是已经入栈了,如果入了,那就不需要再进栈了,我们可以访问它了(这时候左叶子已经出栈且被访问了)。这里不得不说一下python类的扩展性了,可以定义局部类属性,无疑使这个标志位的添加问题轻松解决(虽然如果不做说明的话,很难理解)。
后序遍历:
递归算法:
class Solution:
# @param root, a tree node
# @return a list of integers
def postorderTraversal(self, root,result=[]):
if root==None:
return result
else:
Solution.postorderTraversal(self,root.left,result)
Solution.postorderTraversal(self,root.right,result)
result.append(root.val)
return result非递归算法:
代码5
class Solution:
# @param root, a tree node
# @return a list of integers
def postorderTraversal(self, root):
result=[]
stack=[]
if root!=None:
stack.append(root)
root.be=False
while(len(stack)!=0):
root=stack.pop(len(stack)-1)
if root.be==True:
result.append(root.val)
else:
stack.append(root)
if root.right!=None:
stack.append(root.right)
root.right.be=False
if root.left!=None:
stack.append(root.left)
root.left.be=False
root.be=True
return result
代码5的思路和代码4基本是一样的,这里就不详细讲了,类似代码2的思路不太适合后序遍历,还没太想好。
更新:
之前总觉得后序遍历是最难的,还要搞标识位。最近又看到了新的方法,其实是自己语言掌握的不好,所谓后序遍历不过是根节点在左右孩子的后面,而既然我们顺序访问总会先访问根节点,那我们就先把根节点访问了,之后访问孩子的时候,把节点值插到根节点的前面不就可以了,这一点用Python异或是C++太容易实现了。
class Solution:
# @param root, a tree node
# @return a list of integers
def postorderTraversal(self, root):
stack=[]
result=[]
if root!=None:
stack.append(root)
while len(stack)!=0:
length=len(stack)
tmp_node=stack.pop(length-1)
result.insert(0,tmp_node.val)
if tmp_node.left!=None:
stack.append(tmp_node.left)
if tmp_node.right!=None:
stack.append(tmp_node.right)
return result这个思路和代码1很相像,访问完根节点就舍弃,其实只有录入节点值那一句不一样,这样看来后序遍历也就不难了。换句话说“左右中”的遍历结果和“中右左”的恰好是相反的,这样就更好理解了。

本文介绍了使用Python在LeetCode上进行树的遍历,包括前序、中序和后序遍历。重点讨论了非递归的前序遍历方法,通过模拟递归利用栈来实现。同时提到了递归和非递归中序遍历的实现,并指出后序遍历的一种简洁解法,利用Python特性简化操作。
1301

被折叠的 条评论
为什么被折叠?



