leetcode
leetcode 2451 ~ 2500
元素和最小的山形三元组 II

元素和最小的山形三元组 II

难度:

标签:

题目描述

You are given a 0-indexed array nums of integers.

A triplet of indices (i, j, k) is a mountain if:

  • i < j < k
  • nums[i] < nums[j] and nums[k] < nums[j]

Return the minimum possible sum of a mountain triplet of nums. If no such triplet exists, return -1.

 

Example 1:

Input: nums = [8,6,1,5,3]
Output: 9
Explanation: Triplet (2, 3, 4) is a mountain triplet of sum 9 since: 
- 2 < 3 < 4
- nums[2] < nums[3] and nums[4] < nums[3]
And the sum of this triplet is nums[2] + nums[3] + nums[4] = 9. It can be shown that there are no mountain triplets with a sum of less than 9.

Example 2:

Input: nums = [5,4,8,7,10,2]
Output: 13
Explanation: Triplet (1, 3, 5) is a mountain triplet of sum 13 since: 
- 1 < 3 < 5
- nums[1] < nums[3] and nums[5] < nums[3]
And the sum of this triplet is nums[1] + nums[3] + nums[5] = 13. It can be shown that there are no mountain triplets with a sum of less than 13.

Example 3:

Input: nums = [6,5,4,3,4,5]
Output: -1
Explanation: It can be shown that there are no mountain triplets in nums.

 

Constraints:

  • 3 <= nums.length <= 105
  • 1 <= nums[i] <= 108

代码结果

运行时间: 111 ms, 内存: 30.7 MB


// 思路:使用Java Stream API来计算符合条件的三元组,并且找到最小的元素和。我们将遍历每个中心元素,通过流操作来过滤并找到左右两边的最大值
import java.util.stream.IntStream;

public int minimumMountainTripletSumStream(int[] nums) {
    int n = nums.length;
    return IntStream.range(1, n - 1)
        .map(j -> {
            int leftMax = IntStream.range(0, j)
                .filter(i -> nums[i] < nums[j])
                .map(i -> nums[i])
                .max()
                .orElse(Integer.MIN_VALUE);

            int rightMax = IntStream.range(j + 1, n)
                .filter(k -> nums[k] < nums[j])
                .map(k -> nums[k])
                .max()
                .orElse(Integer.MIN_VALUE);

            return (leftMax != Integer.MIN_VALUE && rightMax != Integer.MIN_VALUE) ? leftMax + nums[j] + rightMax : Integer.MAX_VALUE;
        })
        .min()
        .orElse(-1);
}

解释

方法:

该题解采用了一种改进的暴力搜索方法,用以寻找元素和最小的山形三元组。算法通过单次遍历,同时维护三个指针 left, i, right 分别代表三元组中的三个元素的位置。left 指针始终指向遍历到当前位置 i 前的最小值,而 right 指针则尝试在位置 i 后找到一个小于 nums[i] 的最小元素。每次循环,首先检查 left 指针是否应该更新(即,如果 nums[i] 更小,则更新 left 为当前 i)。然后检查和更新 right 指针,确保 right 指向 i 右侧的最小值,这需要在每次 i 改变时从新从数组尾部向 i 扫描。如果在 i 处的 nums[left] 和 nums[right] 与 nums[i] 形成山形结构,更新最小和 ans。

时间复杂度:

O(n^2)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在更新'left'指针的条件是'nums[left] >= nums[i]',而不是简单地选择'i'之前的最小值?有什么特别的考虑吗?
这里的关键是保持左指针left始终指向当前遍历到的元素之前的最小值。当遍历到新的元素nums[i]时,如果该元素比nums[left]小或等,这意味着我们找到了一个更小的元素,因此更新left指针为当前索引i。这样做是为了确保当我们在考虑任何中心点i时,left指针指向的是i左侧的最小值,从而使得三元组形成山形结构的可能性增大。如果简单地选择i之前的最小值而不更新left,可能会错过更新的机会,导致不是最优的三元组。
🦆
在查找'right'指针的最小值时,为什么选择从数组的尾部向'i'扫描而不是使用其他更有效的方法如二分查找或维护一个有序结构?
在这个算法实现中,选择从数组尾部向i扫描主要是因为它简单且在遍历过程中易于维护。使用二分查找或有序结构虽然可以提高查找效率,但这样做通常需要额外的数据结构(如平衡树或堆),这会增加实现的复杂性和空间复杂度。此外,每次迭代i时,nums[i]右侧的数组部分都在变化,这使得维护一个动态更新的有序结构变得更加复杂。从尾部向前扫描是一个时间复杂度为O(n)的操作,虽然不是最优,但在整体上保持了算法的简洁性。
🦆
如果'nums'数组中存在多个相同的最小值,这种方法是否还能正确地返回元素和最小的山形三元组?
如果数组中存在多个相同的最小值,该方法依然可以正确地返回元素和最小的山形三元组。在这种情况下,left指针会在遇到第一个最小值时停止更新。即使后续存在相同的最小值,left指针的位置已经足够保证它是最小的,并且可以与后续的i和right形成有效的山形三元组。算法的核心在于保证left和right正确地指向了能形成山形三元组的元素,而不受具体数值是否相同的影响。
🦆
在实际操作中,如果'right'指针始终没有更新(即始终不满足'nums[right] < nums[i]'的条件),算法是如何处理这种情况的?
在算法中,如果right指针始终没有更新,即在i的右侧没有找到比nums[i]小的元素,这意味着不能形成山形结构,因此这种情况下不会更新最小和ans。算法会继续遍历其它的i值,直到找到一个合适的right使得nums[right] < nums[i]。如果整个遍历过程中始终找不到符合条件的三元组,算法将返回初始设置的'inf'值,表示不存在有效的山形三元组。这通常会在最后的返回语句中被处理,如果ans仍为'inf',则返回-1,表示无解。

相关问题