算法练习day51

56 阅读3分钟

一、下一个更大元素2

循环数组,索引对数组长度取余,模拟循环数组的遍历

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var nextGreaterElements = function(nums) {
    let result = new Array(nums.length).fill(-1)
    let stack = [0]
    for(let i = 0; i < 2 * nums.length;i++) {
        while(stack.length && nums[i % nums.length] > nums[stack[0]]) {
            let index = stack.shift()
            result[index] = nums[i % nums.length]
        }
        stack.unshift(i % nums.length)
    }
    return result
};

二、接雨水

暴力解法

按照列计算比较容易理解

每一列雨水的高度,取决于该列左侧最高的柱子和该列右侧最高的柱子中最矮的那个和该列的高度差

我们遍历所有的列,第一个和最后一个柱子不接雨水

时间复杂度为O(n^2),空间复杂度为O(1)

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function (height) {
    let sum = 0
    for (let i = 1; i < height.length - 1; i++) {
        let leftHeight = height[i]
        let rightHeight = height[i]
        for (let l = i - 1; l >= 0; i--) {
            leftHeight = Math.max(leftHeight, height[l])
        }
        for (let r = i + 1; r < height.length; r++) {
            rightHeight = Math.max(rightHeight, height[r])
        }
        let h = Math.min(leftHeight, rightHeight) - height[i]
        if (h > 0) {
            sum += h
        }
    }
    return sum
};

双指针优化

暴力解法在遍历到每个柱子都会向两边遍历一遍,其实有些重复计算了,我们把每个位置的左边最高高度记录到一个数组上maxLeft,把右边最高高度记录到一个数组上maxRight,这样避免重复计算

  1. 从左向右遍历,maxLeft[i] = Math.max(height[i], maxLeft[i-1])
  2. 从右向左遍历,maxRight[i] = Math.max(height[i], maxRight[i+1])
var trap = function (height) {
    let sum = 0
    let maxLeft = new Array(height.length).fill(0)
    let maxRight = new Array(height.length).fill(0)
    maxLeft[0] = height[0]
    for (let i = 1; i < height.length; i++) {
        maxLeft[i] = Math.max(height[i], maxLeft[i - 1])
    }
    maxRight[height.length - 1] = height[height.length - 1]
    for (let i = height.length - 2; i >= 0; i--) {
        maxRight[i] = Math.max(height[i], maxRight[i + 1])
    }
    for (let i = 1; i < height.length - 1; i++) {
        let h = Math.min(maxLeft[i], maxRight[i]) - height[i]
        if (h > 0) {
            sum += h
        }
    }
    return sum
};

单调栈解法

单调栈用来寻找任意一个元素右边或者左边第一个比自己大或者小的元素的位置

该题用的单调栈的顺序为从栈头开始,从小到大,当遇到大于栈头的元素,此时出现凹槽,栈头元素就是凹槽的底部的柱子,栈头的第二个元素是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子

遇到相同的柱子也应该弹出栈头,更新下标,因为我们计算宽度需要用最右边的柱子

栈里只需要保存下标,通过下标获取高度

有三种情况

  1. 当前元素高度小于栈顶元素的高度,把当前元素下标放入栈中

  2. 当前元素高度等于栈顶元素的高度,放入栈中

  3. 当前元素高度大于栈顶元素的高度,出现凹槽,弹出栈顶,标记为mid,即为凹槽底部高度,当前栈顶即为底部左侧高度,当前元素即为底部右侧高度

var trap = function (height) {
    let sum = 0
    let stack = [0]
    for (let i = 1; i < height.length; i++) {
        while (stack.length && height[i] > height[stack[0]]) {
            let mid = stack.shift()
            if (stack.length) {
                let left = stack[0]
                let h = Math.min(height[left], height[i]) - height[mid]
                let w = i - left - 1
                sum += h * w
            }
        }
        stack.unshift(i)
    }
    return sum
}