一、递归法
递归三部曲:
1、确定递归函数的参数和返回值
void recursion(TreeNode *root)
2、确定终止条件
当前节点为空的时候,就返回
if(!root) return;
3、确定单层递归的逻辑
//这里是前序遍历
cout << root->val << endl;
preorder(root->left);
preorder(root->right);
我们知道,前序遍历二叉树就是【中左右】,中序遍历二叉树就是【左中右】,后序遍历二叉树就是【左右中】。那么,我们只需要在单层递归里面调换一下递归的顺序即可。
下面就是递归的模板
//前序遍历
void recursion(TreeNode* root){
if(root){
... //root需要做的事情
recursion(root->left);
recursion(root->right);
}
return ;
}
//中序遍历
void recursion(TreeNode* root){
if(root){
recursion(root->left);
... //root需要做的事情
recursion(root->right);
}
return ;
}
//后续遍历
void recursion(TreeNode* root){
if(root){
recursion(root->left);
recursion(root->right);
... //root需要做的事情
}
return ;
}
可以看出,递归方式逻辑很清晰,代码简洁,但是它也有劣势,在递归过程中,如果层级过深,我们很可能保存过多的临时变量,导致栈溢出。这也是为什么我们一般不在后台代码中使用递归的原因。
事实上,函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源,而递归调用,只有走到最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放,如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,导致程序的异常退出。
所以,将递归的方式转换成迭代的方式。请记住,99%的递归转非递归,都可以通过栈来进行实现。
二、迭代法
使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中一种遍历方式,其他两种只要稍微改一下节点顺序就可以了。接下来,让我们一起来捋一捋其中的原因。
使用迭代法的先序遍历:
vector<int> preorderTraversal(TreeNode* root){
stack<TreeNode*> s;
vector<int<