题目的主要信息:
- 给定一颗二叉树的根节点,输出其中序遍历的结果
举一反三:
学习完本题的思路你可以解决如下题目:
方法一:递归(推荐使用)
知识点:二叉树递归
递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。
而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。
思路:
什么是二叉树的中序遍历,简单来说就是“左根右”,展开来说就是对于一棵二叉树,我们优先访问它的左子树,等到左子树全部节点都访问完毕,再访问根节点,最后访问右子树。同时访问子树的时候,顺序也与访问整棵树相同。
从上述对于中序遍历的解释中,我们不难发现它存在递归的子问题,根节点的左右子树访问方式与原本的树相同,可以看成一颗树进行中序遍历,因此可以用递归处理:
- 终止条件: 当子问题到达叶子节点后,后一个不管左右都是空,因此遇到空节点就返回。
- 返回值: 每次处理完子问题后,就是将子问题访问过的元素返回,依次存入了数组中。
- 本级任务: 每个子问题优先访问左子树的子问题,等到左子树的结果返回后,再访问自己的根节点,然后进入右子树。
具体做法:
- step 1:准备数组用来记录遍历到的节点值,Java可以用List,C++可以直接用vector。
- step 2:从根节点开始进入递归,遇到空节点就返回,否则优先进入左子树进行递归访问。
- step 3:左子树访问完毕再回到根节点访问。
- step 4:最后进入根节点的右子树进行递归。
Java实现代码:
import java.util.*;
public class Solution {
public void inorder(List<Integer> list, TreeNode root){
//遇到空节点则返回
if(root == null)
return;
//先去左子树
inorder(list, root.left);
//再访问根节点
list.add(root.val);
//最后去右子树
inorder(list, root.right);
}
public int[] inorderTraversal (TreeNode root) {
//添加遍历结果的数组
List<Integer> list = new ArrayList();
//递归中序遍历
inorder(list, root);
//返回的结果
int[] res = new int[list.size()];
for(int i = 0; i < list.size(); i++)
res[i] = list.get(i);
return res;
}
}
C++实现代码:
class Solution {
public:
void inorder(vector<int> &res, TreeNode* root){
//遇到空节点则返回
if(root == NULL)
return;
//先遍历左子树
inorder(res, root->left);
//再遍历根节点
res.push_back(root->val);
//最后遍历右子树
inorder(res, root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
//递归中序遍历
inorder(res, root);
return res;
}
};
Python实现代码
import sys
class Solution:
def inorder(self, list: List[int], root: TreeNode):
# 遇到空节点则返回
if not root:
return
# 先遍历左子树
self.inorder(list, root.left)
# 再遍历根节点
list.append(root.val)
# 最后遍历右子树
self.inorder(list, root.right)
def inorderTraversal(self , root: TreeNode) -> List[int]:
# 由于python存在最大的递归深度约束,这一步是更改最大深度的限制
sys.setrecursionlimit(1500)
res = [] # 添加遍历结果的list
# 递归中序遍历
self.inorder(res, root)
return res
复杂度分析:
- 时间复杂度:,其中为二叉树的节点数,遍历二叉树所有节点
- 空间复杂度:,最坏情况下二叉树化为链表,递归栈深度为
方法二:非递归(扩展思路)
知识点:栈
栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
思路:
与前序遍历类似,我们利用栈来代替递归。如果一棵二叉树,对于每个根节点都优先访问左子树,那结果是什么?从根节点开始不断往左,第一个被访问的肯定是最左边的节点,
//每次找到最左节点
while(root != NULL){
s.push(root);
root = root->left;
}
然后访问该节点的右子树,最后向上回到父问题。因为每次访问最左的元素不止对一整棵二叉树成立,而是对所有子问题都成立,因此循环的时候自然最开始都是遍历到最左,然后访问,然后再进入右子树,我们可以用栈来实现回归父问题。
具体做法:
- step 1:优先判断树是否为空,空树不遍历。
- step 2:准备辅助栈,当二叉树节点为空了且栈中没有节点了,我们就停止访问。
- step 3:从根节点开始,每次优先进入每棵的子树的最左边一个节点,我们将其不断加入栈中,用来保存父问题。
- step 4:到达最左后,可以开始访问,如果它还有右节点,则将右边也加入栈中,之后右子树的访问也是优先到最左。
图示:
Java实现代码:
import java.util.*;
public class Solution {
public int[] inorderTraversal (TreeNode root) {
//添加遍历结果的数组
List<Integer> list = new ArrayList();
Stack<TreeNode> s = new Stack<TreeNode>();
//空树返回空数组
if(root == null)
return new int[0];
//当树节点不为空或栈中有节点时
while(root != null || !s.isEmpty()){
//每次找到最左节点
while(root != null){
s.push(root);
root = root.left;
}
//访问该节点
TreeNode node = s.pop();
list.add(node.val);
//进入右节点
root = node.right;
}
//返回的结果
int[] res = new int[list.size()];
for(int i = 0; i < list.size(); i++)
res[i] = list.get(i);
return res;
}
}
C++实现代码:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
//辅助栈
stack<TreeNode*> s;
//当树节点不为空或栈中有节点时
while(root != NULL || !s.empty()){
//每次找到最左节点
while(root != NULL){
s.push(root);
root = root->left;
}
//访问该节点
TreeNode* node = s.top();
s.pop();
res.push_back(node->val);
//进入右节点
root = node->right;
}
return res;
}
};
Python实现代码
class Solution:
def inorderTraversal(self , root: TreeNode) -> List[int]:
# s是辅助栈
res, s = [], []
# 当树节点不为空或栈中有节点时
while root or s:
# 每次找到最左节点
while root:
s.append(root)
root = root.left
# 访问该节点
node = s[-1]
s.pop()
res.append(node.val)
# 进入右节点
root = node.right
return res
复杂度分析:
- 时间复杂度:,其中为二叉树的节点数,遍历二叉树所有节点
- 空间复杂度:,辅助栈空间最大为链表所有节点数