递归遍历
主要是考虑递归的三要素
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
//前序遍历的递归函数
public void preorder(TreeNode root, List<Integer> result){
//确定终止条件
if (root == null){
return;
}
//确定单层递归的逻辑
result.add(root.val);
preorder(root.left,result);
preorder(root.right,result);
}
//中序遍历的递归函数
public void inorder(TreeNode root, List<Integer> result){
if (root == null){
return;
}
inorder(root.left,result);
result.add(root.val);
inorder(root.right,result);
}
//后序遍历的递归函数
public void postorder(TreeNode root, List<Integer> result){
if (root == null) return;
postorder(root.left,result);//左
postorder(root.right,result);//右
result.add(root.val);//中
}
迭代遍历
前序遍历
利用栈实现通过迭代对二叉树进行遍历
那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。因为这样出栈的时候才是中左右的顺序。
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
//用栈来实现
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
if (root!=null)
stack.push(root);
//迭代结束条件:栈为空
while(!stack.isEmpty()){
//获取当前要处理的中间节点
TreeNode mid = stack.pop();
result.add(mid.val);
//先把右边的节点入栈,弹出的时候才会是中左右
if (mid.right != null) stack.push(mid.right);
if(mid.left != null) stack.push(mid.left);
}
return result;
}
后续遍历
迭代法的后续遍历可以在前序遍历的基础上进行修改
前序遍历的方式为中-》左-》右,所以需要将右子节点先入栈
而对于后续遍历,先将左子节点入栈,形成中-》右-》左的result,然后将result进行翻转即可
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
if (root!=null){
stack.push(root);
}
while (!stack.isEmpty()){
TreeNode tmp = stack.pop();
result.add(tmp.val);
if (tmp.left!=null){
stack.add(tmp.left);
}
if (tmp.right!=null){
stack.add(tmp.right);
}
}
Collections.reverse(result);
return result;
}
中序遍历
在前序与后序的迭代过程中,实际上有两个操作
- 处理:将元素放进result数组中
- 访问:遍历节点
前序与后序要访问的元素和要处理的元素顺序是一致的,都是中间节点。
那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
//记录当前访问了的节点
TreeNode cur = root;
//迭代停止条件栈为空表示遍历完成,cur!=null表示需要继续访问
while (cur!=null||!stack.isEmpty()){
//如果当前的节点不为空,则移动cur到他的左子节点
if (cur!=null){
stack.push(cur);
cur = cur.left;
}else{
//如果节点没有左子节点了,从栈中弹出栈顶节点
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}