LintCode 126: Max Tree (单调递减栈经典题!)

  1. Max Tree

Given an integer array with no duplicates. A max tree building on this array is defined as follow:

The root is the maximum number in the array
The left subtree and right subtree are the max trees of the subarray divided by the root number.
Construct the max tree by the given array.

Example
Example 1:

Input:[2, 5, 6, 0, 3, 1]
Output:{6,5,3,2,#,0,1}
Explanation:
the max tree constructed by this array is:

    6
   / \
  5   3
 /   / \
2   0   1

Example 2:

Input:[6,4,20]
Output:{20,6,#,#,4}
Explanation:

 20
 / 
6
 \
  4

Challenge
O(n) time and memory.

解法1:
思路:单调递减栈。观察上图可知,

  1. 除了左右端点外,每个数的父节点为其最近的更大的左和右节点中的较小者。比如说0左右比它大的数为6和3,而3<6,若某个端点比其左右两边所有数都大,则其为根节点。
  2. 左端点之父即为右边最近的比它大的数,若没有则其为根节点。
  3. 右端点之父即为左边最近的比它大的数,若没有则其为根节点。

如果我们维护一个单调递减栈(从栈底到栈顶递减),那么:

  1. 当新元素比栈顶元素大时,新元素的左儿子即为从栈顶往下pop,比新元素小的最大的那个,同时记得要不停的pop掉栈顶元素,如此反复,直到栈顶元素大于新元素(此时会执行(2))或栈为空。
  2. 当新元素比栈顶元素小时,不需要pop掉栈顶元素,此时栈顶元素即为新元素左边最近的比它大的数,之右儿子即为新元素。

注意:

  1. 此题不需要在A数组后面加入一个-1。这是与最大直方图题的区别。
  2. MinTree为单调递减栈。
  3. 单调递增栈中可以找到元素左右两侧比自身小的第一个元素。
    单调递减栈中可以找到元素左右两侧比自身大的第一个元素。
  4. 维护一个单调递减单调栈. 如果当前值比stack[-1]要大, 循环弹出栈顶. 当前值是第一个比在弹出值右侧并且比它大的. 所以更新当前节点的左子节点.
    弹出完毕, 如果此时栈非空, 说明栈顶是在当前值左侧第一个比它大的. 栈顶的右子节点应该是当前节点.

from jiuzhang
单调栈可以在O(n)的时间里跑出以每个元素作为最大/小值的最左/右端点下标。
基本过程:
单调栈的操作:以一个递减的单调栈为例,若栈为空或者栈顶元素大于当前元素则压入,否则弹出栈内比当前元素小的所有元素。
以序列{7, 2, 5, 3, 11, 9}为例,模拟一下实现过程。
第一步:栈为空,压入7。 此时栈内:[7]
第二步:7比2大,压入2。 此时栈内:[7, 2]
第三步:2比5小,弹出2,压入5。 此时栈内:[7, 5]
第四步:5比3大,压入3。 此时栈内:[7, 5, 3]
第五步:3、5、7 比11小,弹出3、弹出5、弹出7,压入11。 此时栈内:[11]
第六步:11比9大,压入9 。 此时栈内:[11, 9]
在这个过程中可以发现,栈底元素是栈内元素中的最大值,而最后留在栈内的最底部的元素就是整个数组中的最大元素root。一个元素作为节点时的左儿子也就是左半边最大的值,是这个元素被压入栈时,栈弹出的最后一个元素;而右儿子也就是右半边最大的值,是这个元素的上面一个元素,因为递减的单调栈是从底部往顶部依次减小的。
具体实现:
从前往后依次遍历数组元素,对每个元素:

如果栈内有元素且元素小于当前元素,那么就需要依次弹出栈内比当前元素小的元素。在这个过程中记录最后一个被弹出的元素,这个元素就是当前元素的左儿子。
如果栈内有元素且栈顶元素大于当前元素,那么当前元素就是栈顶元素的右儿子。
最后压入当前元素。
所以我们很容易在这个过程中得到答案,也就是{11,7,9,#,5,#,#,2,3}。

代码如下:

/**
 * Definition of TreeNode:
 * class TreeNode {
 * public:
 *     int val;
 *     TreeNode *left, *right;
 *     TreeNode(int val) {
 *         this->val = val;
 *         this->left = this->right = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode * maxTree(vector<int> &A) {
        
        int len = A.size();
        if (len == 0) return NULL;
        
        stack<int> monoDecStack; // mono decreasing stack
        map<int, TreeNode *> mp; // index, TreeNode *

        for (int i = 0; i < len;  ++i) {
            mp[i] = new TreeNode(A[i]);
            while(!monoDecStack.empty() && (A[monoDecStack.top()] < A[i])) {
                int oldTop = monoDecStack.top();
                monoDecStack.pop();
                mp[i]->left = mp[oldTop];
            }
            if (!monoDecStack.empty() && (A[monoDecStack.top()] > A[i])) {
                mp[monoDecStack.top()]->right = mp[i];
            }
            monoDecStack.push(i);
        }
        
        int lastOne = 0;
        while(!monoDecStack.empty()) {
            lastOne = monoDecStack.top();
            monoDecStack.pop();
        }
     
        return mp[lastOne];
    }
};

其实为了节省时间和空间,不需要用map,用数组就可以了。新代码如下。时间复杂度和空间复杂度都是O(n)。

/**
 * Definition of TreeNode:
 * class TreeNode {
 * public:
 *     int val;
 *     TreeNode *left, *right;
 *     TreeNode(int val) {
 *         this->val = val;
 *         this->left = this->right = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode * maxTree(vector<int> &A) {
        
        int len = A.size();
        if (len == 0) return NULL;
        
        stack<int> monoDecStack; // mono decreasing stack
        vector<TreeNode *> pNodes(len, NULL); // index, TreeNode *

        for (int i = 0; i < len;  ++i) {
            pNodes[i] = new TreeNode(A[i]);
            while(!monoDecStack.empty() && (A[monoDecStack.top()] < A[i])) {
                int oldTop = monoDecStack.top();
                monoDecStack.pop();
                pNodes[i]->left = pNodes[oldTop];
            }
            if (!monoDecStack.empty() && (A[monoDecStack.top()] > A[i])) {
                pNodes[monoDecStack.top()]->right = pNodes[i];
            }
            monoDecStack.push(i);
        }
        
        int lastOne = 0;
        while(!monoDecStack.empty()) {
            lastOne = monoDecStack.top();
            monoDecStack.pop();
        }
     
        return pNodes[lastOne];
    }
};

更简洁的做法;

class Solution {
public:
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode * maxTree(vector<int> &A) {
        int n = A.size();
        vector<TreeNode* > stk;
        for (int i = 0; i < n; ++i) {
            TreeNode* tmpNode = new TreeNode(A[i]);
            while(stk.size() > 0 && A[i] > stk.back()->val) { //如果stk中的最后一个节点比新节点小
                tmpNode->left = stk.back(); //当前新节点的左子树为stk的最后一个节点
                stk.pop_back();
            }
            if (stk.size() > 0) { //如果stk不为空
                stk.back()->right = tmpNode; //将新节点设为stk最后一个节点的右子树
            }
            stk.push_back(tmpNode);
        }
    
        return stk[0];
    }
};

注意:
这题的MaxTree和MaxHeap是不一样的。本题要求以最大节点为分界线,其左右两边分别递归。而maxheapify的话,有可能左右两边的节点跑到对方的子树里面取。
以 https://blog.csdn.net/guoweimelon/article/details/50904346 为例,可以看到原始数据为a[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7},
14本来在16的右边,构建最大堆后,14移到了16的左子树里。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值