二叉树的非递归遍历
二叉树的前序遍历
题目描述
- 题目
给定一个二叉树,返回它的 前序 遍历。
-
示例:
-
输入: [1,null,2,3]
1 \ 2 / 3
-
输出: [1,2,3]
解题
- 思路
- 补充
前序遍历:按照先遍历根结点、再遍历左子结点、最后遍历右子节点的顺序的访问一棵树的结点值
如下例
在遍历某一层结点的时候需要我们在不同的时期去获取同一层的不同结点地址
例如:上图中的B,C结点本在同一层,但是因为特殊的遍历方式需要我们先去遍历B的左右子树再回来获取C的结点地址.
鉴于该遍历方式的特殊性,我们利用栈的 先进后出 特点来完成这个遍历过程
由于我们需要先去遍历左子树再去遍历右子树,所以我们需要先将右子节点压入栈中,再将左子节点压入栈中.
需要注意在将栈顶元素弹出的时候需要将其右、左(先右后左)子节点也压入栈中(若存在右左结点的话)
- 动图演示
代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr)
return ret;
stack<TreeNode*> s;
s.push(root);//先将根结点地址压入栈中
while(!s.empty()){
TreeNode * head=s.top();
s.pop();
//先将根结点的值存储到需要返回的数组中
ret.push_back(head->val);
//将根结点的右子树压入栈中
if(head->right)
s.push(head->right);
//将根结点的左子树压入栈中
if(head->left)
s.push(head->left);
}
return ret;
}
};
-
复杂度分析
-
时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N),其中 N 是节点的个数,也就是树的大小。
-
空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)。
-
提交结果
二叉树的中序遍历
题目描述
- 题目
给定一个二叉树,返回它的中序 遍历。
-
示例:
-
输入: [1,null,2,3]
1 \ 2 / 3
-
输出: [1,3,2]
解题
-
补充
-
中序遍历:先遍历根结点的左孩子结点,再遍历根结点,最后遍历根结点的右孩子结点
-
如下例
-
可以看到我们需要最先输出的结点是值为D的结点,接着是其父亲结点(这里就引出了回退的问题,回退到其根结点),所以我们需要利用栈的 先进后出 的特点先将结点D的根结点B压入栈中,在弹出结点值为B的结点后需要将其右子节点(值为E)压入栈中.
-
循环执行上述步骤,直到栈为空(既将二叉树中的所有结点都压到栈中)
-
动图演示
代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr){
return ret;
}
stack<TreeNode*> s;
TreeNode* cur=root;
while(cur!=nullptr || !s.empty()){
while(cur){
s.push(cur);
cur=cur->left;
}
cur=s.top();
s.pop();
ret.push_back(cur->val);
cur=cur->right;
}
return ret;
}
};
-
复杂度分析
-
空间复杂度:O(N)
-
时间复杂度:O(N)
-
运行结果
二叉树的后序遍历
题目描述
- 题目
给定一个二叉树,返回它的 后序 遍历。
-
示例:
-
输入: [1,null,2,3]
1 \ 2 / 3
-
输出: [3,2,1]
解题
-
补充
-
二叉树的后序遍历:先遍历根结点的左孩子 再遍历根结点的 右孩子, 最后遍历根结点
-
如下例
从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点(按照先左后右的顺序压入子节点),按照从上到下、从左至右的顺序依次压入栈中。
因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。
- 动图演示
- 该过程是未逆置前的存储结果
- 可以发现上述操作过后数组中存储的值顺序和后序遍历的值顺序相反,所以我们需要将数组中的值进行反转
代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr){
return ret;
}
stack<TreeNode*> s;
s.push(root);
while(!s.empty()){
TreeNode* head=s.top();
s.pop();
ret.push_back(head->val);
if(head->left){
s.push(head->left);
}
if(head->right){
s.push(head->right);
}
}
reverse(ret.begin(),ret.end());
return ret;
}
};
-
复杂度分析
-
时间复杂度:访问每个节点恰好一次,因此时间复杂度为 O(N),其中 N 是节点的个数,也就是树的大小。
-
空间复杂度:取决于树的结构,最坏情况需要保存整棵树,因此空间复杂度为 O(N)
-
运行结果