算法 二叉树遍历

45 阅读3分钟

二叉树

节点结构

class TreeNode{
    public int value;
    public TreeNode left;
    public TreeNode right;

    TreeNode(int value){
        this.value = value;
    }
}

二叉树结尾指向 null;

左孩子 右孩子都为空,表示叶子节点;

根节点或者叫头节点;

二叉树

假设一个二叉树结构, 如下图:

image.png

二叉树的遍历递归实现

TreeTest.java 可以看代码注释 中序遍历的栈的方法, 打印方式不同,


        if (headNode != null) {
            Stack<TreeNode> stack = new Stack<>();
            while (!stack.isEmpty() || headNode != null) {
                if (headNode != null) {
                    stack.push(headNode);
                    headNode = headNode.left;
                } else {
                    headNode = stack.pop();
                    System.out.print(headNode.value + ",");
                    headNode = headNode.right;
                }
            }
        }
  • 先序遍历(根左右): 在递归序的链表下, 第一次出现的打印,重复出现都不打印,得到的结果;

  • 中序遍历(左根右): 在递归序的链表下, 只有第二次出现的打印, 其他不打印,得的结果;

  • 后续遍历(左右根): 在递归序的链表下, 只有第三次出现打印,其他不打印,得到的结果;

二叉树栈实现;

先序遍历:根左右

  • 从栈中弹出 current 节点

  • 打印 current

  • 把当前节点先压右 再压左(如果有)

  • 重复以上操作

image.png

    /**
     * 前序
     * 
     * @param headNode
     */
    public void preOrderRecur1(TreeNode headNode) {
        if (headNode != null) {
            Stack<TreeNode> stack = new Stack<>();
            stack.push(headNode);
            while (!stack.isEmpty()) {
                //打印当前节点的时候,当前结点的右结点 、 左结点进栈;
                headNode = stack.pop();
                System.out.print(headNode.value + ",");
                if (headNode.right != null) {
                    stack.push(headNode.right);
                }
                if (headNode.left != null) {
                    stack.push(headNode.left);
                }
            }
        }
    }
    

中序遍历: 左根右

  • 每颗子树,整个树左边界进站;

  • 依次弹出节点的过程中,打印(处理),对弹出节点右树 周而复始;

image.png

    /**
     * 中序
     * 
     * @param headNode
     */
    public void midOrderRecur1(TreeNode headNode) {
        if (headNode != null) {
            Stack<TreeNode> stack = new Stack<>();
            while (!stack.isEmpty() || headNode != null) {
                if (headNode != null) {
                    stack.push(headNode);
                    headNode = headNode.left;
                } else {
                    headNode = stack.pop();
                    System.out.print(headNode.value + ",");
                    headNode = headNode.right;
                }
            }
        }
    }

后序遍历:左右根

  • 两个栈;

  • 第一个栈入栈顺序是 先压左 后压右;

  • 第二个栈 是第一个栈的逆序,就是先压右边,在压左;最后出栈就是左右头;

操作顺序

  • 弹出 current

  • 把current 压入第二个栈

  • 先压左再压右边;

  • 重复以上顺序;

TreeTest.java 代码递归和栈实现二叉树的三种遍历


import java.util.Stack;

class TreeTest {

    /**
     * 
     * 
     * 
     * @param args
     */
    public static void main(String args[]) {
        TreeNode headNode = new TreeNode(5);
        headNode.left = new TreeNode(3);
        headNode.right = new TreeNode(8);
        headNode.left.left = new TreeNode(2);
        headNode.left.right = new TreeNode(4);
        headNode.left.left.left = new TreeNode(1);
        headNode.right.left = new TreeNode(7);
        headNode.right.left.left = new TreeNode(6);
        headNode.right.right = new TreeNode(10);
        headNode.right.right.left = new TreeNode(9);
        headNode.right.right.right = new TreeNode(11);
        System.out.println("前序遍历 --->");
        new TreeTest().preOrderRecur(headNode);
        System.out.println("\n");
        new TreeTest().preOrderRecur1(headNode);
        System.out.println("\n");
        System.out.println("中序遍历 --->");
        new TreeTest().midOrderRecur(headNode);
        System.out.println("\n");
        new TreeTest().midOrderRecur1(headNode);
        System.out.println("\n");
        System.out.println("后序遍历 --->");
        new TreeTest().endOrderRecur(headNode);
        System.out.println("\n");
        new TreeTest().endOrderRecur1(headNode);
        System.out.println("\n");
    }

    /**
     * 递归顺序
     * 
     * @param headNode
     */
    public void f(TreeNode headNode) {
        if (headNode == null) {
            return;
        }
        // 第一次出现 节点
        f(headNode.left);
        // 第二次出现 节点
        f(headNode.right);
        // 第三次出现 节点
    }

    /**
     * 先序遍历
     * 
     * 根左右
     * 
     * @param headNode
     */
    public void preOrderRecur(TreeNode headNode) {
        if (headNode == null) {
            return;
        }
        // 第一次出现 节点  入栈时打印当前结点
        System.out.print(headNode.value + " , ");
        preOrderRecur(headNode.left);
        // 第二次出现 节点
        preOrderRecur(headNode.right);
        // 第三次出现 节点
    }

    /**
     * 中序遍历
     * 
     * 左根右
     * 
     * @param headNode
     */
    public void midOrderRecur(TreeNode headNode) {
        if (headNode == null) {
            return;
        }
        // 第一次出现 节点
        midOrderRecur(headNode.left);
        // 第二次出现 节点  左结点出站,右结点入栈的时候打印当前结点
        System.out.print(headNode.value + " , ");
        midOrderRecur(headNode.right);
        // 第三次出现 节点
    }

    /**
     * 后序遍历
     * 
     * 左右根
     * 
     * @param headNode
     */
    public void endOrderRecur(TreeNode headNode) {
        if (headNode == null) {
            return;
        }
        // 第一次出现 节点
        endOrderRecur(headNode.left);
        // 第二次出现 节点
        endOrderRecur(headNode.right);
        // 第三次出现 节点  出栈的时候打印当前就是后序遍历
        System.out.print(headNode.value + " , ");
    }

    /**
     * 前序
     * 
     * @param headNode
     */
    public void preOrderRecur1(TreeNode headNode) {
        if (headNode != null) {
            Stack<TreeNode> stack = new Stack<>();
            stack.push(headNode);
            while (!stack.isEmpty()) {
                //
                headNode = stack.pop();
                System.out.print(headNode.value + ",");
                if (headNode.right != null) {
                    stack.push(headNode.right);
                }
                if (headNode.left != null) {
                    stack.push(headNode.left);
                }
            }
        }
    }

    /**
     * 中序
     * 
     * @param headNode
     */
    public void midOrderRecur1(TreeNode headNode) {
        if (headNode != null) {
            Stack<TreeNode> stack = new Stack<>();
            while (!stack.isEmpty() || headNode != null) {
                if (headNode != null) {
                    stack.push(headNode);
                    headNode = headNode.left;
                } else {
                    headNode = stack.pop();
                    System.out.print(headNode.value + ",");
                    headNode = headNode.right;
                }
            }
        }
    }

    /**
     * 后序
     * 
     * @param headNode
     */
    public void endOrderRecur1(TreeNode headNode) {
        if (headNode != null) {
            Stack<TreeNode> stack1 = new Stack<>();
            Stack<TreeNode> stack2 = new Stack<>();
            stack1.push(headNode);
            while (!stack1.isEmpty()) {
                headNode = stack1.pop();
                stack2.push(headNode);
                if (headNode.left != null) {
                    stack1.push(headNode.left);
                }

                if (headNode.right != null) {
                    stack1.push(headNode.right);
                }
            }
            while (!stack2.isEmpty()) {
                System.out.print(stack2.pop().value + ",");
            }
        }
    }

    // 有序数组转二叉树 二分法数组
    public TreeNode sortedArrayToBST(int[] nums) {
        return helper(nums, 0, nums.length - 1);
    }

    public TreeNode helper(int[] nums, int left, int right) {
        if (left > right) {
            return null;
        }
        // 总是选择中间位置左边的数字作为根节点
        int mid = (left + right) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = helper(nums, left, mid - 1);
        root.right = helper(nums, mid + 1, right);
        return root;
    }
}