二叉树经典算法总结

二叉树经典算法总结

二叉树和经典算法的关系

实际上,快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历。下面详细说明。

快速排序的逻辑是,若要对 nums[lo..hi] 进行排序,我们先找一个分界点 p,通过交换元素使得 nums[lo..p-1] 都小于等于 nums[p],且 nums[p+1..hi] 都大于 nums[p],然后递归地去 nums[lo..p-1]nums[p+1..hi] 中寻找新的分界点,最后整个数组就被排序了。

代码框架

void sort(int[] nums, int lo, int hi) {
    /****** 前序遍历位置 ******/
    // 通过交换元素构建分界点 p
    int p = partition(nums, lo, hi);
    /************************/

    sort(nums, lo, p - 1);
    sort(nums, p + 1, hi);
}

不难发现,这个分界点p就像二叉树中的根节点。

再说说归并排序的逻辑,若要对 nums[lo..hi] 进行排序,我们先对 nums[lo..mid] 排序,再对 nums[mid+1..hi] 排序,最后把这两个有序的子数组合并,整个数组就排好序了。

代码框架

void sort(int[] nums, int lo, int hi) {
    int mid = (lo + hi) / 2;
    sort(nums, lo, mid);
    sort(nums, mid + 1, hi);

    /****** 后序遍历位置 ******/
    // 合并两个排好序的子数组
    merge(nums, lo, mid, hi);
    /************************/
}

先把左右节点分别排序最后进行一个整合,实际上就是二叉树的后序遍历,并且和分治算法相应证。

总结:二叉树的算法本质就是递归,合理利用递归即可解决问题。


经典算法题

leetcode226翻转二叉树

链接:https://leetcode-cn.com/problems/invert-binary-tree/

解题思路

通过题目实际上很容易发现这里的翻转就是指左右子节点对称位置进行交换即可。那我们先考虑base case,即根节点左右子节点利用临时节点交换,后面递归实现即可。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        // 判断二叉树为空
        if (root == null) {
            return null;
        }
        // base case
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        // 递归子节点
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

leetcode116填充每个节点的下一个右侧节点指针

解题思路

本题需要实现类似层级遍历,使得除了每层最右边节点,剩下的节点都指向右边相邻节点。如果只是简单递归链接左右子节点,只能在左右子树分别实现而不能使得左右子树有关联,所以需要单独对左右子树的右节点和左节点进行链接。

代码实现
/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    public Node connect(Node root) {
        if (root == null) {
            return null;
        }
        connectTwoNode(root.left, root.right);
        return root;
    }
    public void connectTwoNode(Node node1, Node node2) {
        if (node1 == null || node2 == null) {
            return;
        }
        // 交换节点算法
        node1.next = node2;
        // 递归同一父节点下的左右节点
        connectTwoNode(node1.left, node1.right);
        connectTwoNode(node2.left, node2.right);
        // 交换相邻节点,这样就可以覆盖所有层所有节点
        connectTwoNode(node1.right, node2.left);
    }
}

leetcode114二叉树展开为链表

2021-06-07_193247

解题思路

本题需要解决的问题是将二叉树进行展开并按照先序遍历的顺序展示在根节点的右子树。问题细化,实际上就是将二叉树的左右子树分别展开,然后现将左子树连接到根节点的右孩子;把原来的右子树拼接到左子树末端。伪代码如下:

  • base case;
  • 问题细化,利用函数定义将左右子树分别进行展开;
  • 将左子树作为根节点右孩子连接,再***找出当前右子树末端节点***,最后拼接右子树到末端。
代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public void flatten(TreeNode root) {
        // base case
        if (root == null) {
            return;
        }
        // 根据函数定义细分问题,分别处理左右子树
        flatten(root.left);
        flatten(root.right);
        // 后序遍历处理子树
        TreeNode left = root.left;
        TreeNode right = root.right;
        root.left = null;
        root.right = left;
        // 将原先的右子树接到当前右子树的末端
        TreeNode p = root;
        // 遍历找到当前右子树最后一个节点
        while (p.right != null) {
            p = p.right;
        }
        // 拼接上原来已经展开的右子树到现在的右子树末端
        p.right = right;
    }
}

leetcode654最大二叉树

解题思路

根据题意可以知道我们需要不断找出除掉本层root外的最大值,然后通过函数定义分别将数组左右两部分做一个最大二叉树操作即可,当然需要中间分隔的索引。具体代码如下:

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }
    public TreeNode build (int[] nums, int lo, int hi) {
        // base case
        if (nums == null) {
            return null;
        }   
        int max = Integer.MIN_VALUE;
        int index = -1;
        // 找到当前数组中最大值和其对应索引值
        for (int i = lo; i <= hi; i++) {
            if (max < nums[i]) {
                index = i;
                max = nums[i];
            }
        }
        TreeNode root = new TreeNode(max);
        root.left = build(nums, lo, index - 1);
        root.right = build(nums, index + 1, hi);
        return root;
    }
}

leetcode105从前序与中序遍历序列构造二叉树

解题思路

首先明确题意,需要通过前序遍历和中序遍历来确定整个二叉树。所以首先是确定root节点,这个很容易,就是前序遍历的第一个元素。接着通过根节点的值来确定左子树和右子树的位置,通过一个索引作为间隔划分开来。然后就是遍历构造出左右子树,注意左右子树分别在前序和中序遍历中索引位置即可,最好是画图,更加准确。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }
    // 构造函数,确定函数功能就是产生并返回二叉树
    public TreeNode build(int[] preorder, int preStart, int preEnd, 
                          int[] inorder, int inStart, int inEnd) {
        if (preStart > preEnd) {
            return null;
        }
        int index = -1;
        int leftSize = 0;
        // 找到根节点
        TreeNode root = new TreeNode(preorder[preStart]);
        // 遍历找到中序遍历中的根节点索引值
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == preorder[preStart]) {
                index = i;
                break;
            }
        }
        leftSize = index - inStart;
        root.left = build(preorder, preStart + 1, preStart + leftSize, inorder, inStart, index - 1);
        root.right = build(preorder, preStart + leftSize + 1, preEnd, inorder, index + 1, inEnd);
        return root;
    }
}

对照图像查看左右子树位置。

leetcode106从中序与后序遍历序列构造二叉树

与上题一样,画图找到索引解决,思路略。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return build(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);
    }
    public TreeNode build(int[] inorder, int inStart, int inEnd, 
                          int[] postorder, int postStart, int postEnd) {
        // base case
        if (inStart > inEnd) {
            return null;
        }
        int index = -1;
        int rightSize = 0;
        TreeNode root = new TreeNode(postorder[postEnd]);
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == postorder[postEnd]) {
                index = i;
                break;
            }
        }
        rightSize = inEnd - index;
        root.left = build(inorder, inStart, index - 1, postorder, postStart, postEnd - rightSize - 1);
        root.right = build(inorder, index + 1, inEnd, postorder, postEnd - rightSize, postEnd - 1);
        return root;
    }
}

leetcode652寻找重复的子树

解题思路

首先同样先解决根节点需要做些什么。根据题意,我们需要找到所有重复的子树,所以必须先把所有子树进行记录,并记录下每个子树出现的次数,超过1次及以上都要存储最后返回即可。当然,为了得到每一个子树的具体构造,就需要根据后序遍历构造出。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 记录所有子树以及出现的次数
    HashMap<String, Integer> memo = new HashMap<>();
    // 记录重复的子树根节点
    LinkedList<TreeNode> res = new LinkedList<>();
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        traverse(root);
        return res;
    }
    /* 辅助函数 */
    public String traverse(TreeNode root) {
        // base case
        if (root == null) {
            return "#";
        }
        // 先通过拼接字符串的方式把二叉树序列化,分别构造左右子树
        // 然后再加上root的值
        String left = traverse(root.left);
        String right = traverse(root.right);
        String subTree = left + "," + right+ "," + root.val;
        int freq = memo.getOrDefault(subTree, 1);
        // 多次重复也只会被加入结果集一次
        if (freq == 2) {
            res.add(root);
        }
        // 给子树对应的出现次数加一
        memo.put(subTree, freq + 1);
        return subTree;
    }
}

二叉搜索树

BST(Binary Search Tree)特性:

1、对于 BST 的每一个节点 node,左子树节点的值都比 node 的值要小,右子树节点的值都比 node 的值大。

2、对于 BST 的每一个节点 node,它的左侧子树和右侧子树都是 BST。

注意:BST的中序遍历结果是有序的(升序)。

void traverse(TreeNode root) {
    if (root == null) return;
    traverse(root.left);
    // 中序遍历代码位置
    print(root.val);
    traverse(root.right);
}

leetcode230二叉搜索树中第K小的元素

解题思路

根据BST的特性我们可以知道通过升序排序之后就可以很方便找出第k个元素,在本题中就是对应第k小的元素。而升序排序其实就是中序遍历。在中序遍历时记录每个元素的排名并通过比较找出第k个元素即可。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 记录结果
    int res = 0;
    // 记录排名
    int rank = 0;
    public int kthSmallest(TreeNode root, int k) {
        traverse(root, k);
        return res;
    }
    public void traverse(TreeNode root, int k) {
        // base case
        if (root == null) {
            return;
        }
        traverse(root.left, k);
        // 中序遍历代码位置
        rank++;
        if (k == rank) {
            // 找到第 k 小的元素
            res = root.val;
            return;
        }
        /*****************/
        traverse(root.right, k);
    }
}

leetcode538把二叉搜索树转换为累加树

解题思路

对于BST算法题,我们应该想到利用它的两个性质,一个就是其左子树的元素始终小于右子树,二是其中序遍历的结果是有序的。根据这里的题意其实我们不难发现,每一个累加树节点都是从右子树开始累加直到加到自己为止。那么BST的中序遍历就可以利用起来,不过和一般的中序遍历不一样,这里需要从右子树开始。并维护一个sum来记录累加值并重新赋值给每个节点的val。

代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode convertBST(TreeNode root) {
        traverse(root);
        return root;
    }
    // 定义累加记录器
    int sum = 0;
    public void traverse(TreeNode root) {
        // base case
        if (root == null) {
            return;
        }
        // 降序排列BST
        traverse(root.right);
        sum += root.val;
        // 将 BST 转化成累加树
        root.val = sum;
        traverse(root.left);
    }
}

BST的增删改查

判断BST的合法性

在判断是否是BST时,我们不能光判断左节点小于右节点,这样可能导致根节点大于右子树个别元素或者左子树某些元素大于根节点。正确的做法是传入额外参数保证将根节点的限制条件传给左右子节点。

代码实现

boolean isValidBST(TreeNode root) {
    // 注意:这里说明传入辅助函数的参数中,min和max都是null
    return isValidBST(root, null, null);
}

/* 限定以 root 为根的子树节点必须满足 max.val > root.val > min.val */
boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
    // base case
    if (root == null) return true;
    // 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST
    if (min != null && root.val <= min.val) return false;
    if (max != null && root.val >= max.val) return false;
    // 限定左子树的最大值是 root.val,右子树的最小值是 root.val
    return isValidBST(root.left, min, root) 
        && isValidBST(root.right, root, max);
}
在BST中搜索一个数

思路很简单,首先就是遍历实现查找。

代码实现

boolean isInBST(TreeNode root, int target) {
    if (root == null) return false;
    if (root.val == target) return true;
    // 当前节点没找到就递归地去左右子树寻找
    return isInBST(root.left, target)
        || isInBST(root.right, target);
}

但是这样似乎对于所有二叉树都适用,没有利用BST的特性。BST是符合左小右大的。那么其实可以联想到二分查找。同理可以优化代码。

boolean isInBST(TreeNode root, int target) {
    if (root == null) return false;
    if (root.val == target)
        return true;
    if (root.val < target) 
        return isInBST(root.right, target);
    if (root.val > target)
        return isInBST(root.left, target);
    // root 该做的事做完了,顺带把框架也完成了,妙
}

由此可以总结出BST的遍历框架。这个框架对于BST非常实用,建议熟练掌握。代码如下:

void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}
在BST中插入一个数

对于在BST中插入元素或者说所有数据结构中插入元素,只有两个步骤,先找到插入位置再进行插入。而上面的遍历框架就给我们提供查找位置的便利。直接利用即可。思路非常清晰简单。

代码实现

TreeNode insertIntoBST(TreeNode root, int val) {
    // 找到空位置插入新节点
    if (root == null) return new TreeNode(val);
	// 判断val的位置
    if (root.val < val) 
        root.right = insertIntoBST(root.right, val);
    if (root.val > val) 
        root.left = insertIntoBST(root.left, val);
    return root;
}
在 BST 中删除一个数

删除情况比较多,需要分类讨论,具体见代码。

TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return null;
    if (root.val == key) {
        // 这两个 if 把情况 1 和 2 都正确处理了,情况1即没有子节点,2即有一个子节点
        if (root.left == null) return root.right;
        if (root.right == null) return root.left;
        // 处理情况 3,即被删除的节点有两个子节点
        TreeNode minNode = getMin(root.right);
        root.val = minNode.val;
        root.right = deleteNode(root.right, minNode.val);
    } else if (root.val > key) {
        root.left = deleteNode(root.left, key);
    } else if (root.val < key) {
        root.right = deleteNode(root.right, key);
    }
    return root;
}

TreeNode getMin(TreeNode node) {
    // BST 最左边的就是最小的
    while (node.left != null) node = node.left;
    return node;
} 

leetcode98验证二叉搜索树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root, null, null);
    }

    public boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
        // base case
        if (root == null) return true;
        // 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST
        if (min != null && root.val <= min.val) return false;
        if (max != null && root.val >= max.val) return false;
        // 限定左子树的最大值是 root.val,右子树的最小值是 root.val
        return isValidBST(root.left, min, root) 
            && isValidBST(root.right, root, max);
    }
}

leetcode700二叉搜索树中的搜索

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        // base case
        if (root == null) return null;
        if (val == root.val) {
            return root;
        }
        else if (val < root.val) {
            return searchBST(root.left, val);
        }
        else if (val > root.val) {
            return searchBST(root.right, val);
        }
        else return null;
    }
}

leetcode701二叉树中的插入操作

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) {
            return new TreeNode(val);
        }
        if (val > root.val) {
            root.right = insertIntoBST(root.right, val);
        }
        if (val < root.val) {
            root.left = insertIntoBST(root.left, val);
        }
        return root;
    }
}

leetcode450BST删除操作

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) {
            return null;
        }
        if (root.val == key) {
            if (root.left == null) {
                return root.right;
            }
            if (root.right == null) {
                return root.left;
            }
            // 找出最小节点
            TreeNode minNode = getMin(root.right);
            root.val = minNode.val;
            // 删除原来的节点
            root.right = deleteNode(root.right, minNode.val);
        }else if (root.val > key) {
            root.left = deleteNode(root.left, key);
        } else if (root.val < key) {
            root.right = deleteNode(root.right, key);
        }
        return root;
    }
    public TreeNode getMin(TreeNode node) {
        // BST 最左边的就是最小的
        while (node.left != null) node = node.left;
        return node;
    } 
}

leetcode96不同的二叉搜索树

解题思路

很明显这是一个穷举问题。我们首先得找出根节点再确定其他组合。而根节点没有限制,所以所有节点都可以作为根节点。然后根据BST的特性,左小右大,所以小于根节点的作为左子树,大于根节点的作为右子树,至于详细组合只需要递归就行。最后情况就是左右子树组合数相乘即可得到。

代码实现
class Solution {
    public int numTrees(int n) {
        return count(1, n);
    }
    /* 计算闭区间 [lo, hi] 组成的 BST 个数 */
    public int count(int lo, int hi) {
        // base case
        if (lo > hi) return 1;
        int res = 0;
        for (int i = lo; i <= hi; i++) {
            // i 的值作为根节点 root
            int left = count(lo, i - 1);
            int right = count(i + 1, hi);
            // 左右子树的组合数乘积是 BST 的总数
            res += left * right;
        }
        return res;
    }
}

但是这样实际上时间复杂度非常高,存在重叠子问题。为了解决该问题,加上一个备忘录。

代码改进
class Solution {
    // 备忘录的值初始化为 0
    int[][] memo;
    public int numTrees(int n) {
        memo = new int[n + 1][n + 1];
        return count(1, n);
    }

    /* 计算闭区间 [lo, hi] 组成的 BST 个数 */
    public int count(int lo, int hi) {
        // base case
        if (lo > hi) return 1;
        // 查备忘录
        if (memo[lo][hi] != 0) {
            return memo[lo][hi];
        }
        int res = 0;
        for (int i = lo; i <= hi; i++) {
            // i 的值作为根节点 root
            int left = count(lo, i - 1);
            int right = count(i + 1, hi);
            // 左右子树的组合数乘积是 BST 的总数
            res += left * right;
        }
        // 将结果存入备忘录
        memo[lo][hi] = res;
        return res;
    }
}

leetcode95不同的搜索二叉树II

解题思路

1、穷举root节点的所有可能。

2、递归构造出左右子树的所有合法 BST。

3、给root节点穷举所有左右子树的组合。

代码实现
class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) return new LinkedList<>();
        // 构造闭区间 [1, n] 组成的 BST 
        return build(1, n);
    }
    public List<TreeNode> build(int lo, int hi) {
        List<TreeNode> res = new LinkedList<>();
        // base case
        if (lo > hi) {
            res.add(null);
            return res;
        }
        // 1、穷举 root 节点的所有可能。
        for (int i = lo; i <= hi; i++) {
            // 2、递归构造出左右子树的所有合法 BST。
            List<TreeNode> leftTree = build(lo, i - 1);
            List<TreeNode> rightTree = build(i + 1, hi);
            // 3、给 root 节点穷举所有左右子树的组合。
            for (TreeNode left : leftTree) {
                for (TreeNode right : rightTree) {
                    // i 作为根节点 root 的值
                    TreeNode root = new TreeNode(i);
                    root.left = left;
                    root.right = right;
                    res.add(root);
                }
            }
        }
        return res;
    }
}

leetcode1373二叉搜索子树的最大键值和

解题思路

二叉树题目首先想到的就是得明确当前节点需要做什么。在本题中我们先要找到BST,并且找出键值和最大的BST。所以不难看出,我们需要当前节点的如下信息:

1、左右子树是否是 BST。(如果左右子树不是BST就更不用说当前节点作为根节点了)

2、左子树的最大值和右子树的最小值。(判断以当前节点作为根节点还是不是满足BST的特性)

3、左右子树的节点值之和。(求和得出题目要求的键值和)

前文同样说过,对于二叉树的不同遍历方式,主要看你的操作适合哪一种。在这里,每次都需要先操作左右子树然后进行剩下的操作,显然满足后序遍历的思路,所以采用后序遍历应该是最具效率的。

代码实现
class Solution {
    // 全局变量,记录 BST 最大节点之和
    int maxSum = 0;
    public int maxSumBST(TreeNode root) {
        traverse(root);
        return maxSum;
    }
    public int[] traverse(TreeNode root) {
        // base case
        if (root == null) {
            return new int[] {
                1, Integer.MAX_VALUE, Integer.MIN_VALUE, 0
            };
        }

        // 递归计算左右子树
        int[] left = traverse(root.left);
        int[] right = traverse(root.right);

        /******* 后序遍历位置 *******/
        int[] res = new int[4];
        // 这个 if 在判断以 root 为根的二叉树是不是 BST
        if (left[0] == 1 && right[0] == 1 &&
            root.val > left[2] && root.val < right[1]) {
            // 以 root 为根的二叉树是 BST
            res[0] = 1;
            // 计算以 root 为根的这棵 BST 的最小值
            res[1] = Math.min(left[1], root.val);
            // 计算以 root 为根的这棵 BST 的最大值
            res[2] = Math.max(right[2], root.val);
            // 计算以 root 为根的这棵 BST 所有节点之和
            res[3] = left[3] + right[3] + root.val;
            // 更新全局变量
            maxSum = Math.max(maxSum, res[3]);
        } else {
            // 以 root 为根的二叉树不是 BST
            res[0] = 0;
        }
        /**************************/
        return res;
    }
}
null) {
            return new int[] {
                1, Integer.MAX_VALUE, Integer.MIN_VALUE, 0
            };
        }

        // 递归计算左右子树
        int[] left = traverse(root.left);
        int[] right = traverse(root.right);

        /******* 后序遍历位置 *******/
        int[] res = new int[4];
        // 这个 if 在判断以 root 为根的二叉树是不是 BST
        if (left[0] == 1 && right[0] == 1 &&
            root.val > left[2] && root.val < right[1]) {
            // 以 root 为根的二叉树是 BST
            res[0] = 1;
            // 计算以 root 为根的这棵 BST 的最小值
            res[1] = Math.min(left[1], root.val);
            // 计算以 root 为根的这棵 BST 的最大值
            res[2] = Math.max(right[2], root.val);
            // 计算以 root 为根的这棵 BST 所有节点之和
            res[3] = left[3] + right[3] + root.val;
            // 更新全局变量
            maxSum = Math.max(maxSum, res[3]);
        } else {
            // 以 root 为根的二叉树不是 BST
            res[0] = 0;
        }
        /**************************/
        return res;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
叉树遍历是指按照特定顺序访问二叉树中的所有节点,将节点的值输出或者进行其他操作。常见的二叉树遍历方式有三种:先序遍历、中序遍历和后序遍历。下面简单介绍这三种遍历方式的算法设计与实现分析。 1. 先序遍历 先序遍历的遍历顺序是先遍历根节点,然后遍历左子树,最后遍历右子树。先序遍历的算法设计如下: ```python def preorder_traversal(root): if root is not None: print(root.val) # 输出节点值 preorder_traversal(root.left) # 遍历左子树 preorder_traversal(root.right) # 遍历右子树 ``` 先序遍历的时间复杂度为$O(n)$,其中$n$是二叉树中节点的个数。因为需要遍历每个节点一次。 2. 中序遍历 中序遍历的遍历顺序是先遍历左子树,然后遍历根节点,最后遍历右子树。中序遍历的算法设计如下: ```python def inorder_traversal(root): if root is not None: inorder_traversal(root.left) # 遍历左子树 print(root.val) # 输出节点值 inorder_traversal(root.right) # 遍历右子树 ``` 中序遍历的时间复杂度为$O(n)$,其中$n$是二叉树中节点的个数。因为需要遍历每个节点一次。 3. 后序遍历 后序遍历的遍历顺序是先遍历左子树,然后遍历右子树,最后遍历根节点。后序遍历的算法设计如下: ```python def postorder_traversal(root): if root is not None: postorder_traversal(root.left) # 遍历左子树 postorder_traversal(root.right) # 遍历右子树 print(root.val) # 输出节点值 ``` 后序遍历的时间复杂度为$O(n)$,其中$n$是二叉树中节点的个数。因为需要遍历每个节点一次。 总结: 以上三种遍历方式都采用递归的方式实现,时间复杂度都为$O(n)$,其中$n$是二叉树中节点的个数。递归的过程中需要使用栈来保存每个节点的状态,因此空间复杂度也为$O(n)$。如果使用非递归的方式实现二叉树遍历,则时间复杂度为$O(n)$,空间复杂度可以优化为$O(h)$,其中$h$是二叉树的高度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值