二叉树的前、中、后序遍历通用方法
首先给大家复习一下前序、后序、中序是怎么一个遍历顺序。你要记忆的话也很好记,对于一个二叉树的节点,除去父节点那它和他相关的一共就只有左节点、右节点和当前节点(中)三个节点。而不管前、中、后序都是左节点比右节点先遍历,中节点在哪儿就是什么遍历。比如中节点在左右节点中间就叫中序遍历(左中右),在左右节点后面就叫后序遍历(左右中)。
递归:
只要是二叉树,基本上都能用递归来解,因为二叉树它的结构很好地满足了递归的要求。递归的思想就是把原有的一个大问题分成若干个具有相同解法的小问题来求解。把要求解的二叉树想成要求解的大问题,那么求解它的左右子树就是在求解小问题,并且它的左右子树依然具有二叉树的性质也就是具有相同的解法,这不就和递归的思想不谋而合吗?
确定了用递归来求解,那就需要考虑终止条件了,也就是我们不停地往下遍历,什么时候该停止?这个简单,我们自然地可以想到当遇到了空节点的时候就该停止然后往回走了。
OK,只要终止条件确定好了,接下来就是递归的常规操作了,以中序遍历为例,直接上代码! C++实现:
void inorder(TreeNode* cur,vector<int>& ret){
if(cur==NULL)
return;
inorder(cur->left,ret);
ret.push_back(cur->val);
inorder(cur->right,ret);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
inorder(root,ret);
return ret;
}
Python实现:
def postorderTraversal(root):
def postorder(cur,ret):
if cur==None:
return
postorder(cur.left,ret)
ret.append(cur.val)
postorder(cur.right,ret)
ret=[]
postorder(root,ret)
return ret
递归的方法只要思路理通了实现就很容易,使用上面的代码来实现前序和后序遍历只需要把代码当中的可替换部分中的 ret.push_back(cur->val); 放到inorder(cur->left,ret); 的前面就实现了前序遍历,放到inorder(cur->right,ret); 的后面就实现了后续遍历。原理也很简单,无非就是当前节点是否应该先比它的左或右子节点放到遍历结果当中,该就放在前面,不该就放在后面。
迭代:
要知道,系统实现递归是通过栈来实现的,所以我们也一定可以用栈这种数据结构来实现。可问题在于我们要区分还没有遍历左右子树的节点和已经遍历过左右子树的节点,仅有后者才该进行出栈操作。只把节点入栈让我们难以区分,那么我们就多带点信息进去嘛,这里我是多带了一个bool 值,用来区分该节点是不是第一次入栈还是左右节点都已经入栈过再次遇到该退栈这两种情况。先上中序遍历代码: C++:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
stack<pair<TreeNode*,bool>> node_stack;
pair<TreeNode*,bool> tmp;
if(root!=NULL)
node_stack.push(pair<TreeNode*,bool>(root,true));
while(!node_stack.empty()){
tmp=node_stack.top();
node_stack.pop();
if(tmp.second){
if(tmp.first->right!=NULL)
node_stack.push(pair<TreeNode*,bool>(tmp.first->right,true));
node_stack.push(pair<TreeNode*,bool>(tmp.first,false));
if(tmp.first->left!=NULL)
node_stack.push(pair<TreeNode*,bool>(tmp.first->left,true));
}
else
ret.push_back(tmp.first->val);
}
return ret;
}
Python:
def inorderTraversal(root):
ret,stack=[],[]
if root!=None:
stack.append((root,True))
while len(stack)!=0 :
cur,flag=stack.pop()
if flag:
if cur.right!=None:
stack.append((cur.right,True))
stack.append((cur,False))
if cur.left!=None:
stack.append((cur.left,True))
else:
ret.append(cur.val)
return ret
上面是中序遍历的一个实现,但是在代码里面确实先入栈的右节点然后是当前节点,最后才是左节点,这是因为栈是先访问栈顶元素,我们中序遍历顺序是“左中右”所以压入栈的时候就该反着来,这样出栈时候的顺序才和中序遍历要求的一致。
可能大家就要问了,既然我们是用的栈来模拟递归,那为啥递归的时候没有按着相反的顺序来调用?因为递归里面新调用一个函数是放在栈顶的,它只有把栈顶的函数处理完了才会继续处理后面调用的函数,也就是说我们这里用栈来模拟是一下子把三个要处理的都放了进去所以才要反着来以保证左子树在最上面(中序),而递归那里是处理完了左子树再处理当前节点,处理完了当前节点再处理右子树,也就不存在利用栈来模拟时的问题啦!
上面的代码要换成前序和后序也很简单,直接把上面代码的可替换部分,也就是压栈的那一部分的顺序改一下就是了,不过也要注意,入栈和出栈的顺序是相反的,前序的压栈顺序应该是右左中,后序的压栈顺序应当是中右左。
C++的实现里面用到了stack 和pair 两种数据结构
- 对于
stack 它通过stack<type> obj 来创建一个栈,在本例中它还用到了:obj.top() 用来获取栈顶元素,obj.pop() 将栈顶元素弹出(这个与Python的pop() 不同,Python中的是弹出且返回其值,而C++的并不会返回),obj.push(x) 将x 元素压入栈顶 - 对于
pair 它是一个键值对,初始化用pair<type1 type2> obj 在本例中它还使用了:obj.first 用来访问键值对的键,obj.second 用来访问值
Python里面的键值对直接用(key,value) 这样一个形式来实现,在访问的时候要分别获取键和值的话m_key,m_value=(key,value) 就实现了
想验证自己写的程序对不对就在下方的题目里面试试吧! LeetCode二叉树前序遍历 LeetCode二叉树中序遍历 LeetCode二叉树后序遍历
|