二叉树相关算法

题一:

校验一颗二叉树是否是合格的二叉查找树,这是LeetCode的第98题:

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than the node’s key.
Both the left and right subtrees must also be binary search trees.

第一个冒出的想法就是利用中序遍历的方式,一个合格的二叉查找树其中序遍历的结果一定是递增的。

一:利用一个辅助的栈来实现中序遍历。

    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        ArrayDeque<TreeNode> stack = new ArrayDeque<>();
        LinkedList<Integer> list = new LinkedList<>();
        stack.addLast(root);
        root = root.left;
        while(!stack.isEmpty() || root != null){
            if (root != null){
                stack.addLast(root);
                root= root.left;
            }
            else {
                TreeNode top = stack.pollLast();
                Integer last = list.peekLast();
                if(last != null && last >= top.val) return false;
                list.add(top.val);
                root = top.right;
            }
        }
        return true;
    }

二:利用Morris算法实现无辅助空间来实现二叉树的中序遍历

    // 利用morris算法
    public boolean isValidBST2(TreeNode root) {
        TreeNode cur1 = root;
        TreeNode cur2;
        LinkedList<Integer> list = new LinkedList<>();
        while(cur1 != null) {
            cur2 = cur1.left;
            if(cur2 != null) {
                while(cur2.right != null && cur2.right != cur1){
                    cur2 = cur2.right;
                }
                if(cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                }
                else {
                    cur2.right = null;
                    if(cur2.val >= cur1.val) return false;
                }
            }
            Integer last = list.peekLast();
            if(last != null && last >= cur1.val) return false;
            list.add(cur1.val);
            cur1 = cur1.right;
        }
        return true;
    }

三:利用递归的思想
根据二叉查找树的定义,只要在递归过程中确保每个节点符合要求即可

    public boolean isValidBST3(TreeNode root) {
        return isValid(root, Integer.MAX_VALUE, Integer.MIN_VALUE);
    }

    private boolean isValid(TreeNode root, Integer max, Integer min) {
        if (root == null) return true;
        if (root.val <= min || root.val >= max) return false;
        return isValid(root.left, Math.min(root.val, max), min) &&
                isValid(root.right, max, Math.max(root.val, min));
    }

题二:

102:Binary Tree Level Order Traversal
Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).
就是二叉树的层级打印

解法一

    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if(root == null) return result;
        // 利用队列的特性
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(root);
        // 每层结束level都会被赋予新的ArrayList对象
        List<Integer> level = new ArrayList<>();
        // 当前层的结尾
        TreeNode last = root;
        // 下一层的结尾
        TreeNode nLast = null;
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            level.add(node.val);
            if(node.left != null){
                queue.add(node.left);
                nLast = node.left;
            }
            if(node.right != null){
                queue.add(node.right);
                nLast = node.right;
            }
            // 当前层结束,将下一层结尾nLast赋给last,添加结果,赋予level新值
            if(node == last && !queue.isEmpty()){
                last = nLast;
                result.add(level);
                level = new ArrayList<>();
            }
        }
        // 最后一层的level并没有在循环中添加进结果
        result.add(level);
        return result;
    }

这种解法主要利用的就是queue的先进先出特性,你还可以这么写

  public List<List<Integer>> levelOrder(TreeNode root) {
      List<List<Integer>> res = new ArrayList<>();
      if(root == null) return res;
      Queue<TreeNode> queue = new LinkedList<>();
      queue.add(root);
      while(!queue.isEmpty()){
        int size = queue.size();
        List<Integer> temp = new ArrayList<>();
        for(int i=0;i<size;i++){
          TreeNode cur = queue.poll();
          temp.add(cur.val);
          if(cur.left != null) queue.add(cur.left);
          if(cur.right != null) queue.add(cur.right);
        }
        res.add(new ArrayList(temp));
      }
        return res;
  }

解法二:

    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();

        levelOrder(root, 0, list);

        return list;
    }

    public void levelOrder(TreeNode root, int level, List<List<Integer>> list) {
        if (root != null) {
            if (list.size() == level) {
                list.add(new ArrayList<Integer>());
            }

            list.get(level).add(root.val);
            levelOrder(root.left, level + 1, list);
            levelOrder(root.right, level + 1, list);
        }
    }

非常精彩的思路,利用递归加上参数level,实现了在递归过程中节点属于那一层就加到对应的位置去。level是从零开始,它对应存储数组的位置及也从0开始,当level == size为true,说明数组该位置为空,应该先初始化。

题目三

104: Maximum Depth of Binary Tree
二叉树的高度
Given a binary tree, find its maximum depth.

The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

受到题目二的影响,第一个冒出的想法是递归式的层级遍历,在此过程中维持一个对象的不变性,就像题目二的list的size一样,于是一开始我写出的递归是这样的,思路很简单就是当level>max时赋值给max

private void recursion(TreeNode root, int level, int max)

呵呵。。你如何将最新的max值在递归过程中传递。于是将max换成Integer对象,可Integer并没有改变自身值的操作,于是干脆换成list,像题二一样,list的size在递归过程中能够维持不变性,就是最新的更改能够传递不受回溯阶段的影响。于是代码变成了这样

    public int maxDepth(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        recursion(root, 0, list);
        return list.size();
    }
    private void recursion(TreeNode root, int level, List<Integer> list){
        if(root != null){
            if (level == list.size()) list.add(1);
            recursion(root.left, level+1, list);
            recursion(root.right, level+1, list);
        }
    }

这是个糟糕的解法,但是思路是对的,想要在递归过程中维持不变性,将它提到外面就好了,我的意思是成员变量,该题之所以得出上面这样的代码,是应为在解题过程中习惯性将成员变量排出考虑范围。这样一开始利用int max的思路应该这样实现:

    int max;
    public int maxDepth(TreeNode root) {
        recursion(root, 1);
        return max;
    }
    private void recursion(TreeNode root, int level) {
        if(root == null) return;
        if(level > max) max = level;
        recursion(root.left, level + 1);
        recursion(root.right, level + 1);
    }

再进一步,int level也是多余的,该题的思路应该是:maxDepth(TreeNode root)返回的是以root为根的二叉树的高度,那么以root为根的二叉树的高度应该等于什么呢?

height(root) = 1 + max(height(root.left), height(root.right))

所以代码就是:

    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }

题目四:

114: Flatten Binary Tree to Linked List
Given a binary tree, flatten it to a linked list in-place.
原地将二叉树变为链表
解法一:

    // 思路:cur1代表当前节点,让其左子树的最右节点指向cur1的右子树,
    // 然后让其左子树变为其右子树,左子树为null;cur1顺着右链接往下重复上述过程
    public void flatten(TreeNode root) {
        TreeNode cur1 = root;
        while(cur1 != null){
            TreeNode cur2 = cur1.left;
            if(cur2 != null){
                while(cur2.right != null) cur2 = cur2.right;
                cur2.right = cur1.right;
                cur1.right = cur1.left;
                cur1.left = null;
            }
            cur1 = cur1.right;
        }
    }

解法二:
试着将解法一变为递归,分析下解法一的思路:沿着右链接往下,每一个节点都找到其左子树的最右节点,然后进行三步改造操作

    public void flatten2(TreeNode root){
        if (root == null) return;
        flatten2(root.right);
        flatten2(root.left);
        TreeNode left  = root.left;
        TreeNode right = root.right;
        root.left  = null;
        root.right = left;
        while(root.right!=null)
            root = root.right;
        root.right = right;
    }

沿着右链接往下,每一个节点都找到其左子树的最右节点操作的实现就是

flatten2(root.right);
flatten2(root.left);

之后就是对每个节点进行改造,将其左子树变为其右子树,然后找到其最右节点将其将其指向原先的右子树。
经过上述分析,该题就分为两部分:遍历所有节点的手段+将左节点变为右节点连起来。所以递归也可以这么写

    private TreeNode pre;
    public void flatten2(TreeNode root){
        if (root == null) return;
        flatten2(root.right);
        flatten2(root.left);
        root.right = pre;
        root.left = null;
        pre = root;
    }

不要用人脑去跟踪递归过程,不用去想递归过程中树的变化,pre的变化。递归遍历了所有节点,那么我只要记录上一个节点pre,然后将当前节点的右链接指向pre,左链接为null,那么二叉树自然变为一条链表。

题目五

124: Binary Tree Maximum Path Sum
Given a non-empty binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.
1:题目三求高度,问题转化为递归过程中求出当前节点的高度
2:题目四将二叉树转换成链表,将问题转化为再递归过程中,将当前节点的左子树化为右子树,将原先的右子树化为原先左子树的最右节点的右子树。
3:在Kadane’s Algorithm中问题转化为在遍历过程中求以当前位置 i 为结尾的最大子数组的和。

该题的思路有了:递归过程中求出每个节点的最大路径和,而包含当前节点在内的最大路径和maxPath(cur),它的等式如下:

maxPath(cur) = max(0, maxPath(cur.left)) + 
        max(0, maxPath(cur.right)) + cur.val;

用成员变量int max来记录最大值,则最后max一定是这颗二叉树的最大路径和。

     int max = Integer.MIN_VALUE;
     public int maxPathSum2(TreeNode root) {
         recur(root);
         return max;
     }
     private int recur(TreeNode node) {
        if(node == null) return 0;
        int left = Math.max(0, recur(node.left));
        int right = Math.max(0, recur(node.right));
        max = Math.max(max, left+right+node.val);
        return Math.max(left, right) + node.val;``````
    }

猜你喜欢

转载自blog.csdn.net/sinat_34976604/article/details/82229712