想要精通算法和SQL的成长之路 - 下一个更大元素I

想要精通算法和SQL的成长之路 - 下一个更大元素I

前言

想要精通算法和SQL的成长之路 - 系列导航

下一个更大元素 I

原题链接
nums1 中数字 x 的 下一个更大元素 是指 xnums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1nums2 ,下标从 0 开始计数,其中nums1nums2 的子集。对于每个 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 。

我们先来整理一下题干:

  1. nums2数组中首先要找一个元素要满足nums1[i] == nums2[j]
  2. 再找一个元素它的下标要 > j,并满足对应元素值 > nums1[i]

1.1 暴力法

代码怎么写?我们先不去考虑最优的代码解,遇事不决,暴力破解。我们来看看:

  1. 首先,结果数组长度随着nums1。那么就有int[] res = new int[nums1.length]
  2. 其次不存在更大元素的时候,答案是 - 1。存在的时候,就是对应元素值。那么初始化动作就有Arrays.fill(res, -1);
  3. 我们要找到一个下标,满足nums1[i] == nums2[j],得到坐标sameValIndex。再去nums2数组中寻找下标大于sameValIndex,并且值大于nums1的即是我们想要的结果。

那么可得暴力解法:

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    int len = nums1.length;
    int[] res = new int[len];
    // 初始化默认值
    Arrays.fill(res, -1);
    for (int i = 0; i < len; i++) {
        // 赋值最大值,避免在满足nums1[i] == nums2[j]这个条件之前,就进入了第二个if条件的判断。
        // 因为sameValIndex它的值,只有在找到nums1[i] == nums2[j]对应节点的情况下才有意义。
        int sameValIndex = Integer.MAX_VALUE;
        for (int j = 0; j < nums2.length; j++) {
            // 找到值相等的节点下标
            if (nums1[i] == nums2[j]) {
                sameValIndex = j;
            }
            // 在nums2数组,并且在sameValIndex下标之后,寻找第一个更大的元素,找到则退出循环
            if (j > sameValIndex && nums2[j] > nums1[i]) {
                res[i] = nums2[j];
                break;
            }
        }
    }
    return res;
}

1.2 单调栈

上面的暴力法有什么问题?每次for循环都要完整的遍历一遍nums2数组。这样做真的有必要吗?

我们换一个思路,首先我们求得的元素都是在nums2当中找到的。那么这里我们是不是可以先无视nums1数组,以nums2数组当的每一个元素为基准,找到后面第一个比它大的元素。那这样就不需要每次for循环都遍历一遍nums2数组了。也就是说我们做个映射表,这样的操作我们只用一次即可。

  • 寻找下一个比当前元素大的,我们就应该使用单调递增栈
  • 寻找下一个比当前元素小的,我们就应该使用单调递减栈

映射表的创建:

// 单调递增栈
LinkedList<Integer> stack = new LinkedList<>();
// 映射关系表,Key:某个元素,Value:nums2中下一个比它大的元素值
HashMap<Integer, Integer> map = new HashMap<>();
// 记录Map,每个元素和它的下一个更大的元素
for (int num : nums2) {
    // 如果当前元素比栈顶还要大,说明对于栈顶元素来说,当前元素就是下一个比它大的。当然坐标肯定比它大,因为我们是正序遍历
    while (!stack.isEmpty() && stack.peek() < num) {
        // 出栈的同时,记录映射关系
        map.put(stack.pop(), num);
    }
    // 入栈
    stack.push(num);
}
// 上面for循环,我们必定满足每一个元素都只入栈一次。但是无法保证所有元素都出栈成功,因此剩下来的元素都是找不到比它大的,我们给他出栈并赋值-1即可
while (!stack.isEmpty()) {
    map.put(stack.pop(), -1);
}

那么最终结果,我们只需要一个for循环,遍历nums1即可:

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    int len = nums1.length;
    int[] res = new int[len];
    // 单调递增栈
    LinkedList<Integer> stack = new LinkedList<>();
    // 映射关系表,Key:某个元素,Value:nums2中下一个比它大的元素值
    HashMap<Integer, Integer> map = new HashMap<>();
    // 记录Map,每个元素和它的下一个更大的元素
    for (int num : nums2) {
        // 如果当前元素比栈顶还要大,说明对于栈顶元素来说,当前元素就是下一个比它大的。当然坐标肯定比它大,因为我们是正序遍历
        while (!stack.isEmpty() && stack.peek() < num) {
            // 出栈的同时,记录映射关系
            map.put(stack.pop(), num);
        }
        // 入栈
        stack.push(num);
    }
    // 上面for循环,我们必定满足每一个元素都只入栈一次。但是无法保证所有元素都出栈成功,因此剩下来的元素都是找不到比它大的,我们给他出栈并赋值-1即可
    while (!stack.isEmpty()) {
        map.put(stack.pop(), -1);
    }
	// 遍历nums1,找到对应元素在映射表中,下一个更大的值
    for (int i = 0; i < len; i++) {
        res[i] = map.get(nums1[i]);
    }

    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值