二叉树三种遍历递归及非递归实现&层次遍历(Java版)

  1. 二叉树基本概念
    二叉树是每个结点至多有两颗子树的树,子树有左右之分,其次序不能任意颠倒。
  2. 几种特殊二叉树
    1) 满二叉树:高度为h,并且含有2h-1个结点的二叉树。即树的每一层都是满的(每层结点数为2(h-1)) 。若对满二叉树编号,从上自下,从左至右,根节点编号为1,则对于编号为i 的结点,如果有双亲,则双亲为⌊i/2⌋,如果有左孩子,左孩子为2i,如果有右孩子,右孩子为2i+1.如下图所示:
    二叉树三种遍历递归及非递归实现&层次遍历(Java版)_第1张图片
    满二叉树.png

    2) 完全二叉树:若1个高度为h,含有n个结点的二叉树,按照从上自下,从左至右的顺序对结点进行编号,1~n的结点与满二叉树一一对应,则该二叉树为完全二叉树。如下图所示:
    二叉树三种遍历递归及非递归实现&层次遍历(Java版)_第2张图片
    完全二叉树.png

    3) 二叉排序树:左子树上所有关键字均小于根节点关键字,右子树所有结点关键字均大于根节点关键字。左子树和右子树又各是一颗二叉排序树。
    4) 平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。
  3. 二叉树遍历
    二叉树定义
 public class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }
 }

非递归实现借助栈,前序、中序、后序是相对于根节点而言,根据根节点输出的位置,根、左、右为前序遍历;左、根、右为中序遍历;左、右、根为后序遍历。
1) 前序遍历(PreOrder):
如果二叉树为空,则什么也不做;否则:
a)访问根节点;
b)先序遍历左子树;
c)先序遍历右子树。
递归实现:

public List preorderTraversal(TreeNode root) {
    List list = new ArrayList<>();
    if(root == null)
        return list;
    helper(root, list);
    return list;
}
public void helper(TreeNode root, List list) {
    list.add(root.val);
    if(root.left != null) 
        helper(root.left, list);
    if(root.right != null)
        helper(root.right, list);
}

常规版非递归实现:先将根节点入栈,然后弹出(访问根节点),然后将其右结点入栈,再将其左结点入栈(栈是先进后出,我们需要先访问左结点,所以先将右结点入栈),然后弹出左结点,对左结点也进行同样的操作(右结点先入栈,左结点入栈),直至栈为空并且访问完了所有结点。

public List preorderTraversal(TreeNode root) {
    List list = new ArrayList<>();
    if(root == null)
        return list;
    Stack stack = new Stack<>();
    stack.push(root);
    while(!stack.isEmpty()) {
        root = stack.pop();
        list.add(root.val);
        if(root.right != null)
            stack.push(root.right);
        if(root.left != null)
            stack.push(root.left);
    }
    return list;
}

简单版非递归实现:与上一种方式的不同是在访问根节点的时机不相同。常规方式是在出栈时访问根节点,将其写入list中,而简单方式是入栈的同时将值写入list,所以我们只要保证按照前序遍历的顺序去遍历二叉树即可。

    public List preorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        Deque stack = new ArrayDeque<>();
        TreeNode node = root;
        while(node != null || !stack.isEmpty()) {
            if(node != null) {
                stack.push(node);
                list.add(node.val);
                node = node.left;
            }else {
                node = stack.pop();
                node = node.right;
            }
        }
        return list;
    }

2) 中序遍历(InOrder):
如果二叉树为空,则什么也不做;否则:
a)中序遍历左子树;
b)访问根节点;
c)中序遍历右子树。
递归实现:

     public List inorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        if(root == null)
            return list;
        helper(root, list);
        return list;
    }
    public void helper(TreeNode root, List list) {

        if(root.left != null)
            helper(root.left, list);
        list.add(root.val);
        if(root.right != null)
            helper(root.right, list);
    }

常规版非递归实现:从根节点依次遍历左结点,如果左结点没有被访问过,则入栈,当没有左结点或左结点被访问过的时候,弹出栈顶元素,将其写入list,并将其右结点入栈。重复上述操作。

    public List inorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        Map map = new HashMap<>();
        Stack stack = new Stack<>();
        if(root == null) 
            return list;
        stack.push(root);
        while(!stack.isEmpty()) {
            root = stack.peek();
            while(root.left != null) {
                if(map.containsKey(root.left))
                    break;
                stack.push(root.left);
                root = root.left;
            }
            root = stack.pop();
            list.add(root.val);
            map.put(root, 1);
            if(root.right != null)
                stack.push(root.right);
               
        }
        return list;
    }

简单版非递归实现:从根节点依次将左结点入栈,当没有左结点的时候,弹出栈顶元素,将其写入list,并将其右结点入栈。重复上述操作。与常规方法相比,省去了查看左子树是否被访问过的步骤,对每个结点,都是先遍历其左子树,所以当访问到该结点的时候,可以确保其左子树已经被访问过了,只需要访问其本身和其右结点即可。

    public List inorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        Deque stack = new ArrayDeque<>();
        TreeNode node = root;
        while(node!=null || !stack.isEmpty()) {
            if(node != null) {
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                list.add(node.val);
                node = node.right;
            }
        }
        return list;
    }

3) 后序遍历(PostOrder):
如果二叉树为空,则什么也不做;否则:
a)后序遍历左子树;
b)后序遍历右子树;
c)访问根节点。
递归实现:

    public List postorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        if(root == null)
            return list;
        helper(root, list);
        return list;
    }
    public void helper(TreeNode root, List list) {

        if(root.left != null)
            helper(root.left, list);

        if(root.right != null)
            helper(root.right, list);
        list.add(root.val);
    }

常规版非递归实现:将根节点左子树入栈,当访问到叶子结点,出栈,查看该叶子结点的父节点是否有右结点,有的话,如果右结点未访问则将其入栈,当一个节点的左右结点都已经访问过或者不包含左右结点,则出栈。

    public List postorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        Map map = new HashMap<>();
        Stack stack = new Stack<>();
        if(root == null)
            return list;
        stack.push(root);
        while(!stack.isEmpty()) {
            root = stack.peek();
            if(root.left == null && root.right == null) {//不含左右结点时,出栈
                root = stack.pop();
                list.add(root.val);
                map.put(root, 1);
            }else if((root.left!=null && root.right == null && map.containsKey(root.left))||(root.right != null && root.left == null && map.containsKey(root.right)) || (root.left != null && root.right != null && map.containsKey(root.left) && map.containsKey(root.right))){//包含子节点,但是子节点被访问过,出栈
                root = stack.pop();
                list.add(root.val);
                map.put(root, 1);
            }else {
                while(root.left != null) {
                    if(map.containsKey(root.left)) {
                        break;
                    }
                    stack.push(root.left);
                    root = root.left;
                }
                if(root.right != null) {
                   if(map.containsKey(root.right)) {
                        break;
                    }
                    stack.push(root.right);
                }
            }
        }
        return list;
    }

简单版非递归实现:后序遍历的输出顺序是左、右、根,当我们采用先序遍历的方法,但是先遍历右子树,实现的效果是根、右、左,刚好和后序遍历的结果想法,所以我们通过add(0, node)的方式将顺序反序,达到我们想要的效果。

public List postorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        Deque stack = new ArrayDeque<>();
        TreeNode node = root;
        while(node != null || !stack.isEmpty()) {
            if(node != null) {
                stack.push(node);
                list.add(0, node.val);
                node = node.right;
            }else {
                node = stack.pop();
                node = node.left;
            }
        }
        return list;
    }
  1. 层次遍历:将二叉树按层输出,借助队列实现。借助null来标记一层的结束。当读取到的结点不是null,将该结点的左右结点入队列,当读到null,如果此时队列不空,则继续入null标志新的一层的结尾。
    public List> levelOrder(TreeNode root) {
        List> list = new ArrayList<>();
        if(root == null)
            return list;
        Queue queue = new LinkedList<>();
        queue.add(root);
        queue.add(null);
        int num = 0;
        List tempList = new ArrayList<>();
        list.add(tempList);
        while(!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            if(temp != null) {
                list.get(num).add(temp.val);
                if(temp.left != null)
                    queue.add(temp.left);
                if(temp.right != null)
                    queue.add(temp.right);
            }else {
                if(!queue.isEmpty()) {
                    num++;
                    tempList = new ArrayList<>();
                    list.add(tempList);
                    queue.add(null);
                }
            }
        }
        return list;
    }

参考:
Preorder, Inorder, and Postorder Iteratively Summarization
二叉树的几种遍历递归与非递归java实现

你可能感兴趣的:(二叉树三种遍历递归及非递归实现&层次遍历(Java版))