• LeetCode #845 Longest Mountain in Array 数组 线性DP


    Description


    Let's call any (contiguous) subarray B (of A) a mountain if the following properties hold:

    • B.length >= 3
    • There exists some 0 < i < B.length - 1 such that B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
      (Note that B could be any subarray of A, including the entire array A.)

    Given an array A of integers, return the length of the longest mountain.

    Return 0 if there is no mountain.

    Example 1:

    Input: [2,1,4,7,3,2,5]
    Output: 5
    Explanation: The largest mountain is [1,4,7,3,2] which has length 5.
    

    Example 2:

    Input: [2,2,2]
    Output: 0
    Explanation: There is no mountain.
    

    Note:

    • 0 <= A.length <= 10000
    • 0 <= A[i] <= 10000

    Follow up:

    • Can you solve it using only one pass?
    • Can you solve it in O(1) space?



    思路


    解法一

    由题可知,山型数组的 LIS(LDS) 部分是严格递增(递减)的,所以判断条件只能是 < 或 >,不能是 ≤ 或 ≥ 。

    先不考虑什么高级解法,暴力解题。利用一个 for 循环得到每个 increasing sequence 的最后一个元素,称之为 peak,并逐一加入 peak_points 中。随后遍历 peak_points 中的每一个 peak, 继续计算以它为起点的 decreasing sequence 的长度,同时计算出山型数组最长的长度。

    时间复杂度:O(n^2),因为得到了每一个 peak 之后还需要一个循环来处理 decreasing sequence
    空间复杂度:O(n),需要一个 HashMap 存储 peak 的索引和以它为终点的 increasing sequence 的长度

    耗时 28 ms, Memory 9.7 MB, ranking 15%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            // mapping from index of peak element to increasing sequence length
            unordered_map<int, int> peak_points;  
            int incre_seq_length = 1;
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] < nums[i]) {
                    ++incre_seq_length;
                } else {
                    // length of increasing part of B should be at least 2
                    if (incre_seq_length >= 2) {
                        peak_points[i - 1] = incre_seq_length;
                    }
                    
                    incre_seq_length = 1;
                }
            }
            
            // derive length of decresing sequences following start_point
            int max_mountain_length = 0;
            for (auto item : peak_points) {
                int mountain_length = item.second;
                bool has_decre_element = false;
                
                for (int i = item.first + 1; i < nums.size(); ++i) {
                    if (nums[i-1] > nums[i]) {
                        if (!has_decre_element) has_decre_element = true;
                        ++mountain_length;
                    } else {
                        break;
                    }
                }
                
                if (has_decre_element && 
                        max_mountain_length < mountain_length) {
                    max_mountain_length = mountain_length;
                }
            }
            
            return max_mountain_length;
        }
    };
    



    解法二

    利用 DP 求 LIS 的思想求解,逆向求 LIS(相当于求出了一个山型数组的LDS),然后再正向求 LIS,在正向求解时顺便也计算出 mountain_length 。

    利用 up[] 和 down[] 记录了求解 LIS 和 LDS 时每个子序列的长度。up[i] 表示以 nums[i] 为结尾的 increasing sequence 的长度(实际为长度减一,以便计算山型数组的长度),down[i] 表示以 nums[i] 为开头的 decreasing sequence 的长度。

    时间复杂度:O(n),因为求解 LDS 和 LIS 是线性DP
    空间复杂度:O(n),需要用到 up[] 和 down[] 两个记录数组

    耗时 20 ms, Memory 9.0 MB, ranking 91.26%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            int max_mountain_length = 0;
            // up[i] represents length - 1 of increasing sequence that end with up[i]
            vector<int> up(nums.size());
            // down[i] represents length - 1 of decreasing sequence that start with down[i]
            vector<int> down(nums.size());
            
            for (int i = nums.size() - 2; i >= 0; --i) {
                if (nums[i] > nums[i+1]) {
                    down[i] = down[i+1] + 1;
                }
            }
            
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] < nums[i]) {
                    up[i] = up[i-1] + 1;
                    
                    if (down[i] > 0) {
                        int mountain_length = up[i] + down[i] + 1;
                        if (max_mountain_length < mountain_length) {
                            max_mountain_length = mountain_length;
                        }
                    }
                }
            }
            
            return max_mountain_length;
        }
    };
    



    解法三

    follow up 中需要将 space 优化成 O(1),所以不能使用解法二中提到的记录数组 up[] 和 down[]。
    因为每次更新状态时,up[] 和 down[] 中的值不能直接被几个变量替代,所以不能直接压缩空间为 O(1),而是需要重新设计 DP 算法里的状态。

    用 up 表示以 nums[i] 为结尾的 increasing sequence 的长度(实际为长度减一,以便计算山型数组的长度),用 down 表示以 nums[i] 为结尾的 decreasing sequence 的长度。这样,山型数组的长度就等价于 up + down + 1。

    因为 nums 的第一个元素肯定不是 peak (山型数组至少要三个数据,如果第一个元素是 peak,那就只剩山型数组的右半部了),所以从 i=1 开始遍历 nums,计算 up、down 和山型数组长度,在每一次重新爬坡时把 up 和 down 重置为 0。遍历结束后,我们就得到了山型数组最长的长度。

    时间复杂度:O(n)
    空间复杂度:O(1)

    耗时 20 ms, Memory 7.8 MB, ranking 91.26%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            int max_mountain_length = 0;
            // up represents length - 1 of increasing sequence that end with current idx
            int up = 0;
            // down represents length - 1 of decreasing sequence that start with current idx
            int down = 0;
            
            // nums[0] cannot be a peak element
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] == nums[i]) {  // go with a new mountain sequence
                    up = 0;
                    down = 0;
                    continue;
                }
                
                if (nums[i-1] < nums[i]) {
                    if (down > 0) {  // go with a new mountain sequence
                        up = 0;
                        down = 0;
                    }
                    
                    ++up;
                }
                
                if (nums[i-1] > nums[i]) {
                    ++down;
                }
                
                if (up > 0 && down > 0) {
                    int mountain_length = up + down + 1;
                    if (max_mountain_length < mountain_length) {
                        max_mountain_length = mountain_length;
                    }
                }
            }
            
            return max_mountain_length;
        }
    };
    



    参考




  • 相关阅读:
    237. Delete Node in a Linked List
    430. Flatten a Multilevel Doubly Linked List
    707. Design Linked List
    83. Remove Duplicates from Sorted List
    160. Intersection of Two Linked Lists
    426. Convert Binary Search Tree to Sorted Doubly Linked List
    142. Linked List Cycle II
    类之间的关系
    初始化块
    明确类和对象
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/12686673.html
Copyright © 2020-2023  润新知