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

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

  • 前言
  • 一. 下一个更大元素 II
    • 1.1 暴力法
    • 1.2 单调栈

前言

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

一. 下一个更大元素 II

原题链接

给定一个循环数组 numsnums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1

示例 1:

  • 输入: nums = [1,2,1]
  • 输出: [2,-1,2]
  • 解释: 第一个 1 的下一个更大的数是 2;数字 2 找不到下一个更大的数; 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

1.1 暴力法

这里的暴力和 下一个更大元素I 很相似。唯一的不同就是:从两个数组变成了一个数组(只不过可以循环)

整体思路:

  • 两层for循环。
  • 由于数组模拟的是循环数组,因此我们在原数组的基础上再拼接一个数组即可。这里可以用Arrays.copyOf进行拷贝。
public int[] nextGreaterElements(int[] nums) {
    int len = nums.length;
    // 因为循环数组的原因,我们用两个循环拼接成一个大数组
    int[] tmp = Arrays.copyOf(nums, 2 * len);
    // 拷贝后半部分数据
    for (int i = len; i < tmp.length; i++) {
        tmp[i] = nums[i - len];
    }
    int[] res = new int[len];
    for (int i = 0; i < nums.length; i++) {
        // 找不到更大值时的默认值是-1
        res[i] = -1;
        // 从当前下标往后遍历,找到第一个值更大的
        for (int j = i + 1; j < tmp.length - 1; j++) {
            if (tmp[j] > nums[i]) {
                res[i] = tmp[j];
                break;
            }
        }
    }
    return res;
}

当然,也可以不需要拷贝数组,通过 下标的一个取模计算 同样能实现循环。

public int[] nextGreaterElements(int[] nums) {
    int len = nums.length;
    int[] res = new int[len];
    for (int i = 0; i < len; i++) {
        // 找不到更大值时的默认值是-1
        res[i] = -1;
        // 从当前下标往后遍历,找到第一个值更大的
        for (int j = i + 1; j < 2 * len - 1; j++) {
            // 对下标进行取模,就不会越界,同时实现了循环的一个下标计算
            int index = j % len;
            if (nums[index] > nums[i]) {
                res[i] = nums[index];
                break;
            }
        }
    }
    return res;
}

1.2 单调栈

思路:

  1. 发现我们要找下一个比当前元素更大的元素。那么我们就使用单调递增栈
  2. 默认值可以提前给数组赋值-1。
  3. 入栈的是数组下标(记得取模处理)。出栈的条件则是:当前元素 > 栈顶元素(栈顶元素最大)。
  4. 这样每个元素最多遍历两遍,最少一次。可以说是O(n)

最终代码如下:

public int[] nextGreaterElements(int[] nums) {
    int len = nums.length;
    int[] res = new int[len];
    // 赋默认值:-1
    Arrays.fill(res, -1);
    // 单调栈
    LinkedList<Integer> stack = new LinkedList<>();
    // 遍历长度:两个数组长度大小
    for (int i = 0; i < len * 2; i++) {
        // 记得对下标进行取模,否则会越界,因为我们取值来源还是原数组
        int index = i % len;
        // 如果发现 当前元素 > 栈顶(单调递增),说明 当前元素 就是 栈顶元素 的下一个更大的值
        while (!stack.isEmpty() && nums[stack.peek()] < nums[index]) {
            // 弹出其下标,赋值当前元素给返回结果
            res[stack.pop()] = nums[index];
        }
        // 下标索引入栈
        stack.push(index);
    }
    return res;
}

你可能感兴趣的:(算法,数据结构,leetcode)