Leetcode 654.最大二叉树 Maximum Binary Tree(Java)

Leetcode 654.最大二叉树 Maximum Binary Tree(Java)

##Tree##

最大二叉树

普通递归

与普通的构造树方法类似

  • 对于一段序列[l,l+1,...r-1,r],找到序列中的最大元素max和其位置pos,则该序列生成树的根节点的值应该为max
  • 该序列生成树的根节点的左子树应该在子序列[l,l+1,...pos-1,pos]中生成,因此递归处理该子序列,将根节点的左子树指向递归生成的树节点
  • 同理,对于右子树,该序列生成树的根节点的右子树应该在子序列[pos+1,pos+2,...r-1,r]中生成,因此递归处理该子序列,将根节点的右子树指向递归生成的树节点
  • 注意递归出口l > r
  • 序列中,找最大值的处理方法为顺序查找

时间复杂度: O(n^2)
在序列中找最大值的方法为顺序查找O(n),共有n个结点

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }
    
    public TreeNode build(int[] n, int l, int r) {
        if (l > r) return null;
        int index = -1, max = Integer.MIN_VALUE;
        for (int i = l; i <= r; i ++) {
            if (n[i] > max) {
                max = n[i];
                index = i;
            }
        }
        TreeNode res = new TreeNode(max);
        TreeNode left = build(n, l, index - 1);
        TreeNode right = build(n, index + 1, r);
        res.left = left;
        res.right = right;
        return res;
    }
}

ST表递归

普通递归的做法瓶颈是在任意给定的序列中找最大值

因此可以考虑优化该问题,使用ST表优化

ST表的详细内容可以参考OIwiki

ST表基于倍增思想,可以做到O(n*logn)预处理,O(1)的查询

时间复杂度: O(n*logn)

class Solution {
    int[][] f;
    int n, k;
    
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        n = nums.length;
        k = (int)(Math.log(n) / Math.log(2));
        f = new int[n][k + 1];
        for (int j = 0; j <= k; j ++) {
            for (int i = 0; i + (1 << j) - 1 < n; i ++) {
                if (j == 0) f[i][j] = i;
                else {
                    int l = f[i][j - 1], r = f[i + (1 << (j - 1))][j - 1];
                    f[i][j] = nums[l] > nums[r] ? l : r;
                }
            }
        }
        return build(nums, 0, n - 1);
    }
    
    public int query(int[] n, int l, int r) {
        int len = r - l + 1;
        int k = (int)(Math.log(len) / Math.log(2));
        int a = f[l][k], b = f[r - (1 << k) + 1][k];
        return n[a] > n[b] ? a : b;
    }
    
    public TreeNode build(int[] n, int l, int r) {
        if (l > r) return null;
        int temp = query(n, l, r);
        TreeNode res = new TreeNode(n[temp]);
        res.left = build(n, l, temp - 1);
        res.right = build(n, temp + 1, r);
        return res;
    }
}

单调栈

考虑到越大的结点,越是要在后面处理,因此单调栈设置为递减栈,本次处理中,用LinkedList实现栈,但进行的操作绝大部分和普通栈没有区别

  • 遍历所有结点curr,如果遍历结点比栈顶结点小curr.val < stack.peekLast().val,则将该结点直接进栈,该结点curr一定是栈顶结点stack.peek()的右子树部分
  • 遍历结点比栈顶元素大curr.val > stack.peekLast().val,栈顶结点stack.peekLast()一定是该结点curr的左子树部分
    • 栈顶结点出栈temp = stack.pollLast(),这时需要判断curr是否是的栈顶元素stack.peekLast()的右儿子
    • 判断正在遍历结点curr的栈顶结点stack.peekLast()的大小,栈顶结点小于正在遍历的结点sta.peekLast().val < curr.val,栈顶元素sta.peekLast()一定是curr的左子树部分,且栈顶元素的右儿子一定是temp
    • 上述步骤循环至栈顶结点大于正在遍历的结点
    • 栈顶结点大于正在遍历的结点sta.peekLast() > curr.val,则curr的左儿子应该是temp
  • curr结点进栈
  • 遍历完所有结点后,栈中仍有几棵独立的子树,根据单调栈的性质,栈顶元素比栈底元素小,且晚于栈底元素进栈,因此栈顶结点应该是栈底结点的右儿子

时间复杂度: O(n)
每个结点都进栈出栈一次

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        LinkedList<TreeNode> sta = new LinkedList<>();
        TreeNode curr = null;
        for (int i = 0; i < nums.length; i ++) {
            curr = new TreeNode(nums[i]);
            while (!sta.isEmpty() && sta.peekLast().val < nums[i]) {
                TreeNode temp = sta.pollLast();
                if (!sta.isEmpty() && sta.peekLast().val < nums[i]) {
                    sta.peekLast().right = temp;
                } else {
                    curr.left = temp;
                }
            }
            sta.offerLast(curr);
        }
        
        while (!sta.isEmpty()) {
            curr = sta.pollLast();
            if (!sta.isEmpty()) {
                sta.peekLast().right = curr;
            }
        }
        
        return curr;
    }
}

单调栈

LinkedList实现栈

  • 遍历所有结点curr,若栈顶结点的值小于当前遍历结点的值stack.peekLast().val < curr.val,栈顶结点stack.peekLast()应该为curr的左儿子结点,curr.left = stack.pollLast()
    循环该操作至栈顶结点的值大于当前遍历结点的值
  • 栈顶结点的值大于当前遍历结点的值stack.peekLast() > curr.val,且栈不为空时,表示栈顶元素的右儿子暂时应该是curr
  • 将当前遍历结点curr进栈
  • 最后取栈底元素为生成树的根节点,这里可以采用LinkedListpollFirst()操作,这是所有操作中唯一不符合栈操作的操作

时间复杂度: O(n)
每个结点只进栈和出栈一次

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        Deque<TreeNode> stack = new LinkedList<>();
        for(int i = 0; i < nums.length; i++) {
            TreeNode curr = new TreeNode(nums[i]);
            while(!stack.isEmpty() && stack.peek().val < nums[i]) {
                curr.left = stack.pop();
            }
            if(!stack.isEmpty()) {
                stack.peek().right = curr;
            }
            stack.push(curr);
        }
        return stack.isEmpty() ? null : stack.removeLast();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值