• Home
  • About
    • 谢志平 photo

      谢志平

      程序员

    • Learn More
    • Email
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

二叉树的递归与非递归遍历

26 Mar 2018

Reading time ~2 minutes

前言

二叉树节点定义

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode(int val) {
        this.val = val;
    }
}

节点访问定义

public void visit(TreeNode node) {
    System.out.println(node.val);
}

二叉树遍历

前序遍历

遍历顺序:根→左→右节点

(1)前序遍历的递归实现

/**
* 二叉树先序遍历递归实现
* @param root
*/
public void preOrderRecursion(TreeNode root) {
    if(root == null) {
        return;
    }
    visit(root);
    preOrderRecursion(root.left);
    preOrderRecursion(root.right);
}

(2)前序遍历的非递归实现

思路:

1、根节点的null判断

2、将根节点root压入栈

3、while循环,终止条件是stack为空,即栈内元素处理完

4、将栈顶元素pop出,读取并存储结果集

5、将取出的元素的右、左子节点分别压入栈,以便循环出栈时是该节点的左、右子节点

/**
* 二叉树先序遍历非递归实现
* @param root
* @return
*/
public List<Integer> preOrderTraversal(TreeNode root){
    List<Integer> resultList = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    //1、根节点的null判断
    if(root == null) {
        return null;
    }
    //2、将根节点root压入栈
    stack.push(root);

    //3、while循环,终止条件是stack为空,即栈内元素处理完
    while(!stack.isEmpty()) {
    //4、将栈顶元素pop出,读取并存储结果集
        TreeNode tempNode = stack.pop();
        resultList.add(tempNode.val);
        //5、将取出的元素的右、左子节点分别压入栈,以便循环出栈时是该节点的左、右子节点
        if(tempNode.right != null) {
            stack.push(tempNode.right);
        }
        if(tempNode.left != null) {
            stack.push(tempNode.left);
        }
    }
    return resultList;
}

后序遍历

(1)后序遍历的递归实现

/**
* 后序遍历的递归实现
* @param root
*/
public void postOrderRecursion(TreeNode root) {
    if (root == null) {
        return;
    }
    postOrderRecursion(root.right);
    postOrderRecursion(root.left);
    visit(root);
}

(2)后序遍历的非递归实现

思路:后序遍历的非递归实现与先序遍历的非递归实现原理基本一致,后序遍历的顺序是左→右→根,而先序遍历的顺序是根→左→右,因此将后序遍历的顺序做一个反转reverse则变成了根→右→左,其实现原理等同于先序遍历。

1、根节点的null判断

2、将根节点root压入栈

3、while循环,终止条件是stack为空,即栈内元素处理完

4、将栈顶元素pop出,读取并存储结果集

5、将取出的元素的左、右子节点分别压入栈,以便循环出栈时是该节点的右、左子节点

6、将结果集进行反转得到后序遍历的结果集

/**
* 后序遍历的非递归实现
* @param root
* @return
*/
public List<Integer> postOrderTraversal(TreeNode root){
    List<Integer> resultList = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    // 1、根节点的null判断
    if(root == null) {
        return null;
    }
    // 2、将根节点root压入栈
    stack.push(root);
    // 3、while循环,终止条件是stack为空,即栈内元素处理完
    while(!stack.isEmpty()) {
        // 4、将栈顶元素pop出,读取并存储结果集
        TreeNode tempNode = stack.pop();
        resultList.add(tempNode.val);
        // 5、将取出的元素的左、右子节点分别压入栈,以便循环出栈时是该节点的右、左子节点
        if(tempNode.left != null) {
            stack.push(tempNode.left);
        }
        if(tempNode.right != null) {
            stack.push(tempNode.right);
        }
    }
    //6、将结果集进行反转得到后序遍历的结果集
    Collections.reverse(resultList);
    return resultList;
}

中序遍历

(1)中序遍历的递归实现

/**
* 中序遍历的递归实现
* @param root
*/
public void midOrderRecursion(TreeNode root) {
    if(root == null) {
        return;
    }
    
    midOrderRecursion(root.left);
    visit(root);
    midOrderRecursion(root.right);
}

(2)中序遍历的非递归实现 中序遍历的费递归实现过程与前序和后序不太一样,中序遍历的遍历顺序是左→根→右

思路:

1、对root进行null值判断

2、while循环,终止条件为stack为空,即站内所有元素都处理完,或者root!=null

3、首先针对当前节点,一直对其左子节点树遍历并将其非空节点入栈

4、节点指针迭代为空(到底了)后不再入栈,然后取栈顶元素,存取结果集

5、将当前指针用出栈的节点的右子节点替代,然后回到第3步继续迭代,直到当前节点为空,且栈也为空即遍历完成

/**
* 中序遍历的非递归实现
*
* @param root
* @return
*/
public List<Integer> midOrderTraversal(TreeNode root) {
    List<Integer> resultList = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    // 1、对root进行null值判断
    if (root == null) {
        return null;
    }
    // 2、while循环,终止条件为stack为空,即站内所有元素都处理完,或者root!=null
    while (!stack.isEmpty() || root != null) {
        // 3、首先针对当前节点,一直对其左子节点树遍历并将其非空节点入栈
        while (root != null) {
            stack.push(root);
            root = root.left;
        }
        // 4、节点指针迭代为空(到底了)后不再入栈,然后取栈顶元素,存取结果集
        TreeNode tempNode = stack.pop();
        resultList.add(tempNode.val);
        // 5、将当前指针用出栈的节点的右子节点替代,然后回到第3步继续迭代,直到当前节点为空,且栈也为空即遍历完成
        root = root.right;
    }
    return resultList;
}

二叉树的层序遍历

/**
* 层序遍历
* @param root
* @return
*/
public List<Integer> leverTraversal(TreeNode root) {
    List<Integer> resultList = new ArrayList<>();
    Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
    if (root == null) {
        return null;
    }
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode tempNode = queue.peek();
        resultList.add(tempNode.val);
        if (root.left != null) {
            queue.add(root.left);
        }
        if (root.right != null) {
            queue.add(root.right);
        }
        queue.poll();
    }
return resultList;
}

二叉树的深度

/**
* 计算二叉树的深度
*
* @param root
* @return
*/
public int countBinaryTreeDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int nleft = countBinaryTreeDepth(root.left);
    int nright = countBinaryTreeDepth(root.right);
    return (nleft > nright) ? (nleft + 1) : (nright + 1);
}


二叉树递归与非递归遍历 Share Tweet +1