方法一:递归遍历
先序遍历
1. 返回值
2. 终止条件 root == null
3. 单层循环 遍历根节点 + 左孩子 + 右孩子
void postorder(TreeNode root) {
if (root != null) {
visit(root); // 访问根节点
postorder(root.left);
postorder(root.right);
}
}
中序遍历
void postorder(TreeNode root) {
if (root != null) {
postorder(root.left);
visit(root); // 访问根节点
postorder(root.right);
}
}
后序遍历
void postorder(TreeNode root) {
if (root != null) {
postorder(root.left);
postorder(root.right);
visit(root); // 访问根节点
}
}
方法二:迭代遍历
1. 迭代遍历中,先序和中序入栈顺序完全相同:一路向左,到头转右
2. 唯一的不同在于访问节点的时机不同,先序在入栈时,中序在出栈时
先序遍历
1. 若左子树不为空,则左子树依次入栈,并访问该节点
2. 若左子树为空,则栈顶元素出栈
3. 判断栈顶元素右子树是否为空,不为空则入栈,即右行一步
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode p = root; // 遍历指针
while(p!=null || !stack.empty()){
if(p!=null){ // 左子树不为空,一直向左
result.add(p.val); // 处理节点,值入结果集
stack.push(p);
p = p.left;
}else{ // 左子树为空,出栈并转向右子树
p = stack.pop();
p = p.right;
}
}
return result;
}
中序遍历
1. 一路向左
2. 出栈并访问
3. 右节点入栈
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode p = root; // 遍历指针
while(p!=null || !stack.empty()){
if(p!=null){ // 左子树不为空,一直向左
stack.push(p);
p = p.left;
}else{ // 左子树为空,出栈并转向右子树
p = stack.pop();
result.add(p.val); // 处理节点,值入结果集
p = p.right;
}
}
return result;
}
后序遍历
1. 一路向左
2. 读栈顶元素,有孩子不空且未被访问,执行右子树
3. 否则,出栈并访问
public List<Integer> postTraversal(TreeNode root){
List result = new LinkedList(); // 结果集
Stack stack = new Stack(); // 借助栈
TreeNode p = root; // 遍历指针
TreeNode r = null; // 记录上一个被访问的节点
while(!stack.empty() || p!=null){
if(p!=null){ // 一路向左
stack.push(p);
p = p.left;
}else{
//读栈顶元素,有孩子不空且未被访问,执行右子树
p = (TreeNode) stack.peek();
if(p.right != null && p.right != r){
p = p.right;
stack.push(p);
p = p.left;
}else{ // 否则,出栈并访问
p = (TreeNode) stack.pop();
result.add(p.val);
r=p;
p=null;
}
}
}
return result;
}
方法三:统一迭代遍历
1. 问题:三种迭代遍历方式代码书写的差异较大,原因是遍历节点和处理节点不能同时通过栈解决。
2. 思路:通过对节点的标记,将遍历节点和处理节点都进行入栈操作,但是对将要处理的节点做好标记
3. 处理方式:将要处理的节点后放入一个空指针座作为标记
4. 好处:三种遍历方式只需要在访问节点后加null做标记,再依此按照遍历顺序重新入栈即可
先序遍历
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>(); // 结果集
if(root == null){
return result;
Stack<TreeNode> stack = new Stack(); // 借助栈
stack.push(root); // 根节点入栈
while(!stack.empty()){
TreeNode top = (TreeNode) stack.peek(); // 获取栈顶元素出栈
if(top != null){ // 栈顶元素不为空,进行访问操作
stack.pop(); // 该节点弹出
if(top.right!=null){ // 按照需要遍历的顺序依此加入左右孩子节点
stack.push(top.right); // 先序遍历:右左中节点依此入栈
}
if(top.left != null){
stack.push(top.left);
}
/*
1. 在中节点后加null,表示该节点被访问过但是未被处理
2. 遍历的代码不同之处只是top + null 再次入栈的顺序不同
*/
stack.push(top);
stack.push(null);
}else{ // 遇到null说明该节点被访问但尚未处理,操作放入结果集
stack.pop(); // 将null弹出
TreeNode temp = (TreeNode) stack.pop(); // 处理节点弹出
result.add(temp.val); // 加入结果集
}
}
return result;
}
中序遍历
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>(); // 结果集
if(root == null){
return result;
Stack<TreeNode> stack = new Stack(); // 借助栈
stack.push(root); // 根节点入栈
while(!stack.empty()){
TreeNode top = (TreeNode) stack.peek(); // 获取栈顶元素出栈
if(top != null){ // 栈顶元素不为空,进行访问操作
stack.pop(); // 该节点弹出
if(top.right!=null){ // 按照需要遍历的顺序依此加入左右孩子节点
stack.push(top.right); // 先序遍历:右中左节点依此入栈
}
stack.push(top);
stack.push(null); // 在中节点后加null,表示该节点被访问过但是未被处理
if(top.left != null){
stack.push(top.left);
}
}else{ // 遇到null说明该节点被访问但尚未处理,操作放入结果集
stack.pop(); // 将null弹出
TreeNode temp = (TreeNode) stack.pop(); // 处理节点弹出
result.add(temp.val); // 加入结果集
}
}
return result;
}
后序遍历
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>(); // 结果集
if(root == null){
return result;
Stack<TreeNode> stack = new Stack(); // 借助栈
stack.push(root); // 根节点入栈
while(!stack.empty()){
TreeNode top = (TreeNode) stack.peek(); // 获取栈顶元素出栈
if(top != null){ // 栈顶元素不为空,进行访问操作
// stack.pop(); // 出栈再入栈,不必操作
// stack.push(top);
stack.push(null); // 在中节点后加null,表示该节点被访问过但是未被处理
if(top.right!=null){ // 按照需要遍历的顺序依此加入左右孩子节点
stack.push(top.right); // 先序遍历:右中左节点依此入栈
}
if(top.left != null){
stack.push(top.left);
}
}else{ // 遇到null说明该节点被访问但尚未处理,操作放入结果集
stack.pop(); // 将null弹出
TreeNode temp = (TreeNode) stack.pop(); // 处理节点弹出
result.add(temp.val); // 加入结果集
}
}
return result;
}