题目地址:
https://leetcode.com/problems/maximum-binary-tree/
给定一个数组
A
A
A,要求按照下列规则进行建一个”最大树“:
1、root是数组中的最大数字;
2、左子树由最大数字左边的数构成,并且左子树也是个最大树;
3、右子树由最大数字右边的数构成,并且右子树也是个最大树;
这一题有一个最快的 O ( n ) O(n) O(n)算法。其实这题本质上是构建一个笛卡儿树。请参考https://blog.csdn.net/qq_46105170/article/details/108526277。以下介绍两个普通做法。
法1:DFS。开一个函数,建立以 A [ l , . . . , r ] A[l,...,r] A[l,...,r]为节点的最大树,这样建树的过程就是,先找到最大值和其下标,然后递归建立左右子树,然后接上去即可。代码如下:
public class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return dfs(nums, 0, nums.length - 1);
}
private TreeNode dfs(int[] nums, int l, int r) {
if (l > r) {
return null;
}
int max = Integer.MIN_VALUE, maxIdx = 0;
for (int i = l; i <= r; i++) {
if (nums[i] > max) {
max = nums[i];
maxIdx = i;
}
}
TreeNode root = new TreeNode(max);
root.left = build(nums, l, maxIdx - 1);
root.right = build(nums, maxIdx + 1, r);
return root;
}
}
class TreeNode {
int val;
TreeNode left, right;
public TreeNode(int val) {
this.val = val;
}
}
时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)(这是最差情况,平均情况的话是 O ( n log n ) O(n\log n) O(nlogn)和 O ( log n ) O(\log n) O(logn))。
法2:线段树 + DFS。由上面的过程我们知道要频繁查询区间最大值以及其下标。这可以用线段树来做。代码如下:
public class Solution {
class SegmentTree {
class Node {
int max, maxIdx;
public Node() {
}
}
private Node[] tree;
private int n;
public SegmentTree(int[] nums) {
tree = new Node[4 * nums.length];
for (int i = 0; i < tree.length; i++) {
tree[i] = new Node();
}
for (int i = 0; i < nums.length; i++) {
update(0, 0, nums.length - 1, i, nums[i]);
}
n = nums.length;
}
private void update(int treeIdx, int l, int r, int idx, int val) {
if (l == r) {
tree[treeIdx].max = val;
tree[treeIdx].maxIdx = idx;
return;
}
int m = l + (r - l >> 1);
int lChild = leftChild(treeIdx), rChild = rightChild(treeIdx);
if (idx <= m) {
update(lChild, l, m, idx, val);
} else {
update(rChild, m + 1, r, idx, val);
}
int lmax = tree[lChild].max, rmax = tree[rChild].max;
int lIdx = tree[lChild].maxIdx, rIdx = tree[rChild].maxIdx;
if (lmax > rmax) {
tree[treeIdx].max = lmax;
tree[treeIdx].maxIdx = lIdx;
} else {
tree[treeIdx].max = rmax;
tree[treeIdx].maxIdx = rIdx;
}
}
// 返回存储nums[qL, qR]内的最大值和其下标包装起来的Node对象
public Node query(int qL, int qR) {
return query(0, 0, n - 1, qL, qR);
}
private Node query(int treeIdx, int l, int r, int qL, int qR) {
if (l == qL && r == qR) {
return tree[treeIdx];
}
int lChild = leftChild(treeIdx), rChild = rightChild(treeIdx);
int m = l + (r - l >> 1);
if (qR <= m) {
return query(lChild, l, m, qL, qR);
} else if (qL >= m + 1) {
return query(rChild, m + 1, r, qL, qR);
}
Node node = new Node();
Node left = query(lChild, l, m, qL, m), right = query(rChild, m + 1, r, m + 1, qR);
if (left.max > right.max) {
node.max = left.max;
node.maxIdx = left.maxIdx;
} else {
node.max = right.max;
node.maxIdx = right.maxIdx;
}
return node;
}
private int leftChild(int idx) {
return 2 * idx + 1;
}
private int rightChild(int idx) {
return 2 * idx + 2;
}
}
private SegmentTree segTree;
public TreeNode constructMaximumBinaryTree(int[] nums) {
segTree = new SegmentTree(nums);
return build(0, nums.length - 1);
}
private TreeNode build(int l, int r) {
if (l > r) {
return null;
}
SegmentTree.Node node = segTree.query(l, r);
int max = node.max, maxIdx = node.maxIdx;
TreeNode root = new TreeNode(max);
root.left = build(l, maxIdx - 1);
root.right = build(maxIdx + 1, r);
return root;
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。建树时间是 O ( n log n ) O(n\log n) O(nlogn),查询最大值及其下标是 O ( log n ) O(\log n) O(logn)。