定义节点:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
Leetcode144. 二叉树的前序遍历
给定一个二叉树,返回它的 前序 遍历
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
递归:
public class Solution {
// 递归
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
tree(root, list);
return list;
}
public void tree(TreeNode root, ArrayList<Integer> list){
if(root!=null){
list.add(root.val);
tree(root.left, list);
tree(root.right, list);
}
}
}
非递归:
过程:(以下图为例,不以leetcode题目为例)
初态为空
- 节点1入栈。
- 出栈,输出栈顶节点1,并将1的左、右孩子节点(2和4)入栈;右孩子先入栈,左孩子后入栈;因为对左孩子的访问要先于右孩子,后入栈的会先出栈访问。
- 出栈,输出栈顶节点2,并将2的左、右孩子节点(3和5)入栈。
- 出栈,输出栈顶节点3,3为叶子节点,无孩子,本步无节点入栈。
- 出栈,输出栈顶节点5,5为叶子节点,无孩子,本步无节点入栈。
- 出栈,输出栈顶节点4,栈空,结束。
- 遍历序列为1,2,3,5,4
迭代代码:
//迭代
public List<Integer> preorderTraversal2(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr= root;
// 若是空树,则直接返回空列表
if(curr==null){
return list;
}
// 非空树,入栈
stack.push(curr);
// 以栈是否为空,作为结束条件
while(!stack.isEmpty()){
// 弹栈
curr = stack.pop();
// 添加值
list.add(curr.val);
if(curr.right!=null){
// 右节点入栈
stack.push(curr.right);
}
if(curr.left!=null){
// 左节点入栈
stack.push(curr.left);
}
}
return list;
}
Leetcode94. 二叉树的中序遍历
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
递归:
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
tree(root, list);
return list;
}
public void tree(TreeNode root, ArrayList<Integer> list){
if(root!=null){
if(root.left!=null){
tree(root.left, list);
}
list.add(root.val);
if(root.right!=null){
tree(root.right, list);
}
}
}
非递归:(以前序遍历例子为例)
初态为空
- 节点1入栈,1左孩子存在
- 节点2入栈,2左孩子存在
- 节点3入栈,3左孩子不存在
- 出栈,输出栈顶节点3,3右孩子不存在
- 出栈,输出栈顶节点2,2右孩子存在,节点5入栈,5左孩子不存在
- 出栈,输出栈顶节点5,5右孩子不存在
- 出栈,输出栈顶节点1,1右孩子存在,节点4入栈,4左孩子不存在
- 出栈,输出栈顶节点4,栈空结束
遍历顺序:3,2,5,1,4
非递归代码
/**
* 利用栈
* 大致过程
* 1、开始根节点入栈
* 2、循环进行以下操作:如果栈顶节点左孩子存在则左孩子入栈;如果栈顶左孩子不存在则出栈并访问栈顶节点,
* 然后检查其右孩子是否存在,如果存在则右孩子入栈
* 3、栈空时结束
* @param root
* @return
*/
public List<Integer> inorderTraversal2(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr!=null || !stack.isEmpty()){
while(curr!=null){
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
list.add(curr.val);
curr = curr.right;
}
return list;
}
145. 二叉树的后序遍历
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
还是以之前的例子为例。
首先手工写出图中二叉树的先序和后序遍历的序列。
先序遍历序列:1、2、3、5、4
后序遍历序列:3、5、2、4、1
把后序遍历序列逆序得:
逆后序遍历序列:1、4、2、5、3
观察发现,逆后序遍历序列和先序遍历序列有一定的联系,逆后序遍历序列只不过是先序遍历过程中对左右子树遍历顺序交换所得到的结果,如下所示。
因此,只需要将前边讲到的非递归先序遍历算法中对左右子树的遍历顺序交换就可以得到逆后序遍
历序列,然后将逆后序遍历序列逆序就得到了后序遍历序列。因此我们需要两个栈,一个栈stack1用来
辅助做逆后序遍历(将先序遍历的左、右子树遍历顺序交换的遍历方式称为逆后序遍历)并将遍历结果
序列压入另一个栈stack2,然后将stack2中的元素全部出栈,所得到的序列即为后序遍历序列。具体的进出栈过程如下图。
初态栈空。
- 结点1入stack1
- stack1 元素出栈,并将出栈结点1入stack2,结点1的左、右孩子存在,左孩子结点2入stack1,右孩子结点4入stack1,这里注意和先序遍历进出栈过程对比,恰好是将其左、右孩子入栈顺序调换,以实现访问顺序的调换。
- stack1 元素出栈,并将出栈结点4入stack2,结点4的左、右孩子不存在。
- stack1 元素出栈,并将出栈结点2入stack2,结点2的左、右孩子存在,左孩子结点3入stack1,右孩子结点5入stack1。
- stack1 元素出栈,并将出栈结点5入stack2。
- stack1 元素出栈,并将出栈结点3入stack2。
此时stack1空,stack2 中元素自顶向下依次为: 3、5、2、4、1,正好为后序遍历序列。
代码:
递归:
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
tree(root, list);
return list;
}
public void tree(TreeNode root, ArrayList<Integer> list){
if(root!=null){
tree(root.left, list);
tree(root.right, list);
list.add(root.val);
}
}
非递归:
public List<Integer> postorderTraversal2(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
TreeNode curr = root;
if(root==null){
return list;
}
stack1.push(curr);
while(!stack1.isEmpty()){
curr = stack1.pop();
stack2.push(curr);
if(curr.left!=null){
stack1.push(curr.left);
}
if(curr.right!=null){
stack1.push(curr.right);
}
}
while (!stack2.isEmpty()){
list.add(stack2.pop().val);
}
return list;
}