Swift - LeetCode - 下一个更大元素 I

56 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

题目

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素。如果不存在下一个更大元素,那么本次查询的答案是 -1

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素

示例 1:

  • 输入: nums1 = [4,1,2], nums2 = [1,3,4,2]
  • 输出: [-1,3,-1]
  • 解释: nums1 中每个值的下一个更大元素如下所述:
  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

  • 输入: nums1 = [2,4], nums2 = [1,2,3,4]
  • 输出: [3,-1]
  • 解释: nums1 中每个值的下一个更大元素如下所述:
  • 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
  • 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

方法一:暴力

思路及解法

根据题意,我们发现 nums1\textit{nums}_1 是一个查询数组,逐个查询 nums2\textit{nums}_2 中元素右边的第一个更大的值。因此,我们可以暴力地逐个计算 nums1\textit{nums}_1 中的每个元素值 nums1[i]\textit{nums}_1[i]nums2\textit{nums}_2 中对应位置的右边的第一个比 nums1[i]\textit{nums}_1[i] 大的元素值。具体地,我们使用如下方法:

  • 初始化与 nums1\textit{nums}_1 等长的查询数组 res\textit{res}

  • 遍历 nums1\textit{nums}_1 中的所有元素,不妨设当前遍历到元素为 nums1[i]\textit{nums}_1[i]

    • 从前向后遍历 nums2\textit{nums}_2 中的元素,直至找到 nums2[j]\textit{nums}_2[j] == nums1[i]\textit{nums}_1[i]
    • 从 j+1+1 开始继续向后遍历,直至找到 nums2[k]>nums2[j]\textit{nums}_2[k] > \textit{nums}_2[j],其中 kj+1k \ge j+1
    • 如果找到了 nums2[k]\textit{nums}_2[k],则将 res[i]\textit{res}[i] 置为 nums2[k]\textit{nums}_2[k],否则将 res[i]\textit{res}[i] 置为 1-1
  • 查询数组 res\textit{res} 即为最终结果。

代码

class Solution {
    func nextGreaterElement(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
        let m: Int = nums1.count
        let n: Int = nums2.count
        var res: [Int] = Array.init(repeating: -1, count: m)
        for i in 0..<m {
            var j: Int = 0
            while j < n && nums2[j] != nums1[i] {
                j += 1
            }
            var k: Int = j + 1
            while k < n && nums2[k] < nums2[j] {
                k += 1
            }
            res[i] = k < n ? nums2[k] : -1
        }
        return res
    }
}

复杂度分析

  • 时间复杂度:O(mn)O(mn),其中 mmnums1\textit{nums}_1 的长度,nnnums2\textit{nums}_2 的长度。

  • 空间复杂度:O(1)O(1)

方法二:单调栈 + 哈希表

思路

我们可以先预处理 nums2\textit{nums}_2,使查询 nums1\textit{nums}_1 中的每个元素在 nums2\textit{nums}_2 中对应位置的右边的第一个更大的元素值时不需要再遍历 nums2\textit{nums}_2。于是,我们将题目分解为两个子问题:

  • 11 个子问题:如何更高效地计算 nums2\textit{nums}_2 中每个元素右边的第一个更大的值;

  • 22 个子问题:如何存储第 11 个子问题的结果。

解法

我们可以使用单调栈来解决第 11 个子问题。倒序遍历 nums2\textit{nums}_2,并用单调栈中维护当前位置右边的更大的元素列表,从栈底到栈顶的元素是单调递减的。

具体地,每次我们移动到数组中一个新的位置 ii,就将当前单调栈中所有小于 nums2[i]\textit{nums}_2[i] 的元素弹出单调栈,当前位置右边的第一个更大的元素即为栈顶元素,如果栈为空则说明当前位置右边没有更大的元素。随后我们将位置 ii 的元素入栈。

因为题目规定了 nums2\textit{nums}_2 是没有重复元素的,所以我们可以使用哈希表来解决第 22 个子问题,将元素值与其右边第一个更大的元素值的对应关系存入哈希表。

代码

class Solution {
    func nextGreaterElement(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
        var hashmap: [Int : Int] = [:]
        var st: [Int] = []
        var n = nums2.count - 1
        while n >= 0 {
            let num: Int = nums2[n]
            while !st.isEmpty && num >= st.last! {
                st.removeLast()
            }
            hashmap[num] = st.isEmpty ? -1 : st.last
            st.append(num)
            n -= 1
        }
        var res: [Int] = Array.init(repeating: -1, count: nums1.count)
        for i in 0..<nums1.count {
            res[i] = hashmap[nums1[i]] ?? -1
        }
        return res
    }
}

复杂度分析

  • 时间复杂度:O(m+n)O(m + n),其中 mmnums1\textit{nums}_1 的长度,nnnums2\textit{nums}_2 的长度。我们需要遍历 nums2\textit{nums}_2 以计算 nums2\textit{nums}_2 中每个元素右边的第一个更大的值;需要遍历 nums1\textit{nums}_1 以生成查询结果。

  • 空间复杂度:O(n)O(n),用于存储哈希表。