二叉树的遍历

1.递归三部曲
  • 确定递归函数的参数与返回值
  • 确定终止条件
  • 确定单层递归的逻辑
2.二叉树的非递归遍历(中序)

二叉树的遍历分为两个部分:节点的访问以及节点的处理

由于中序遍历的节点访问顺序与处理顺序不一致,因此我们用一个指针来遍历节点,用一个栈来记录指针遍历过的节点

由于指针遍历节点的时候,是根左右的顺序(也就是前序遍历的顺序),指针会一直左走走到null,因此栈中节点的左孩子都是访问过的了

当指针走到null的时候,我们就弹出栈中的节点,记录该节点的值(他的左孩子都遍历过了),并访问其右孩子(因为左孩子都访问过了),即将指针指向右孩子。

然后接着一直向左直到遇到null。

总之,每次遇到null,我们就弹出当前栈顶的节点,并记录该节点的值,并将指针指向它的右孩子。加入节点:当指针遍历到非空的节点,就将其加入到栈中,当栈中节点被弹出的时候,就代表其左子树全部访问过了。

代码书写:

终止条件:当前指针指向null且stack中为空

  • 如果当前指针不为null,就把当前访问的元素加入到栈中,且指针继续往左走
  • 如果当前指针为null,我们就从栈顶弹出元素 (该元素的左子树遍历完了),并加入到结果数组中,并将指针指向它的右孩子

每个节点只会进入栈一次

public List<Integer> InOrderTravelsal(TreeNode root){
    TreeNode cur = root;
    List<Integer> res = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<TreeNode>();

    while(cur != null || stack.size() > 0){
        if(cur != null){
            stack.push(cur);
            cur = cur.left;
        }
        else{
            cur = stack.pop();
            res.add(cur.val);
            cur = cur.right;
        }
    }

    return res;
}
3.二叉树的前序和后序遍历

前序遍历中节点的访问顺序与处理顺序是一致的,我们只需要用一个栈保存待访问(处理)节点即可。

前序是根左右,每次pop栈中节点的同时,将其右孩子,左孩子依次入栈即可。
后序是左右根,我们可以在前序的基础上,让左孩子比右孩子先入栈,那么就是根右左,再倒序即为左右根

public List<Integer> preOrder_NotRecursion(TreeNode root){
    List<Integer> res = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        if(node == null) continue;
        res.add(node.val);

        stack.push(node.right);
        stack.push(node.left);
    }

    return res;
}

public List<Integer> postOrder_NotRecursion(TreeNode root){
    List<Integer> res = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        if(node == null) continue;
        res.add(node.val);

        stack.push(node.left);
        stack.push(node.right);
    }

    List<Integer> res_reverse = new ArrayList<>();
    for(int i = res.size()-1; i >= 0; i--){
        res_reverse.add(res.get(i));
    }

    return res_reverse;
}
4.二叉树的层序遍历

使用队列,以及size记录每一层

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    Deque<TreeNode> dq = new ArrayDeque<>();
    if(root != null) dq.addLast(root);

    while(dq.size() > 0){
        int size = dq.size();
        List<Integer> list = new ArrayList<>();
        while(size-- > 0){
            TreeNode node = dq.pollFirst();
            list.add(node.val);
            if(node.left != null) dq.addLast(node.left);
            if(node.right != null) dq.addLast(node.right);
        }
        res.add(list);
    }

    return res;
}
5.非递归遍历大一统写法
public List<Integer> preOrder(TreeNode root){
    List<Integer> res = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    if(root != null) stack.push(root);

    while(stack.size() > 0){
        TreeNode node = stack.peek();
        //是否为NUll
        if(node != null){
            node = stack.pop();
            //右左根压栈
            if(node.right != null) stack.push(node.right);
            if(node.left != null) stack.push(node.left);

            stack.push(node);
            stack.push(null);
        }
        else{
            stack.pop();
            node = stack.pop();
            res.add(node.val);
        }
    }

    return res;
}

唯一的区别在于根节点的压栈,倒序压栈即可,如前序为根左右,那就右左根压栈

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。
如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个NULL指针作为标记。 这种方法也可以叫做标记法。

你可能感兴趣的:(数据结构,算法,java)