算法学习记录(三十五)

135 阅读2分钟

问:

  1. 实现一个特殊的栈,在实现栈基本功能基础上,再实现返回栈中最小元素的功能。要求:push、pop、getMin的时间复杂度都是O(1)。设计这个特殊栈可以使用现成的栈
  2. 仅用队列实现栈;仅用栈实现队列
  3. 给一个二维数组,其中每个数都是正数,要求从左上走到右下。每一步只能向右或者向下。沿途数字累加。返回最小路径和
  4. 接雨水

解:

  1. 用两个栈来实现,一个栈正常存数据,另外一个栈存当前位置最小值
class DiyStack {
    constructor() {
        this.stackOne = []
        this.stackSmall = []
    }
    push(val) {
        this.stackOne.push(val)
        if (!this.stackSmall.length) {
            this.stackSmall.push(val)
        } else {
            const last = this.stackSmall[this.stackSmall.length - 1]
            val > last ? this.stackSmall.push(last) : this.stackSmall.push(val)
        }
    }
    pop() {
        this.stackSmall.pop()
        return this.stackOne.pop()
    }
    getMin() {
        return this.stackSmall[this.stackSmall.length - 1]
    }
}
  1. 问题一: 两个队列,存值时其中一个队列正常存。取值时,把存值队列的最后一个值保留,其他值全部转移到另外一个队列。然后把最后一个值返回。问题二:存值时,往栈1里放数据。取值时,把栈1数据弹到栈2,从栈2弹数据
class DiyStack {
    constructor() {
        this.queue1 = []
        this.queue2 = []
    }
    push(val) {
        this.queue1.push(val)
    }
    pop() {
        const fromQueue = this.queue1.length ? this.queue1 : this.queue2
        const toQueue = fromQueue === this.queue1 ? this.queue2 : this.queue1
        while (fromQueue.length > 1) {
            toQueue.push(fromQueue.shift())
        }
        return fromQueue.shift()
    }
}

class DiyQueue {
    constructor() {
        this.stack1 = []
        this.stack2 = []
    }
    push(val) {
        this.stack1.push(val)
    }
    shift() {
        if (this.stack2.length) return this.stack2.pop()
        while(this.stack1.length) {
            this.stack2.push(this.stack1.pop())
        }
        return this.stack2.pop() ?? -1
    }
}
// 暴力递归
function getMinSum(arr) {
    let minSum = Infinity
    function getRes(x,y,sum) {
        sum += arr[x][y]
        if (x === arr.length - 1 && y === arr[0].length - 1) {
            minSum = Math.min(sum, minSum)
            return
        }
        if (x === arr.length - 1) {
            getRes(x, y + 1, sum)
        } else if (y === arr[0].length - 1) {
            getRes(x + 1, y, sum)
        } else {
            getRes(x + 1, y, sum)
            getRes(x, y + 1, sum)
        }
    }
    getRes(0,0,0)
    return minSum
}
// 递归改DP
function getMinSumDp(arr) {
    const dp = []
    for (let i = 0; i < arr.length; i++) {
        dp[i] = []
    }
    // 确定base case
    dp[arr.length - 1][arr[0].length - 1] = arr[arr.length - 1][arr[0].length - 1]
    // 从后往前,从下往上遍历
    for (let i = arr.length - 1; i >= 0; i--) {
        for (let j = arr[0].length - 1; j >= 0; j--) {
            if (i === arr.length - 1 && j === arr[0].length - 1) continue
            const val = arr[i][j]
            if (i === arr.length - 1) {
                dp[i][j] = dp[i][j+1]+val
            } else if (j === arr[0].length - 1) {
                dp[i][j] = dp[i+1][j]+val
            } else {
                dp[i][j] = Math.min(dp[i+1][j], dp[i][j+1]) + val
            }
        }
    }
    return dp[0][0]
}
function trap(height) {
    // 假设当前位置高度为5,在左侧的某个地方有个10高度,右侧某个地方有个20高度。那么无论如何,当前位置,
    // 一定可以留住5个单位的水。若当前高度为25,那么就留不住水
    // 结论:对于每个位置来说。[当前位置的高度]和[左、右两侧最高高度]将决定当前位置可以存多少格水
    // 当前位置的水 =  左右侧最高点中更低的那个,减去当前位置高度。若为负数说明存不了水
    
    // 求左右侧最高值
    const curLeftMax = []
    const curRightMax = []
    let sum = 0
    for (let i = 0; i < height.length; i++) {
        if (!curLeftMax.length) {
            curLeftMax.push(height[i])
            continue
        }
        const last = curLeftMax[curLeftMax.length - 1]
        last > height[i] ? curLeftMax.push(last) : curLeftMax.push(height[i])
    }
    for (let i = height.length - 1; i >= 0; i--) {
        if (!curRightMax.length) {
            curRightMax.unshift(height[i])
            continue
        }
        const last = curRightMax[0]
        last > height[i] ? curRightMax.unshift(last) : curRightMax.unshift(height[i])
    }
    for (let i = 0; i < height.length; i++) {
        sum += Math.max(Math.min((curLeftMax[i - 1] ?? 0), (curRightMax[i + 1] ?? 0)) - height[i], 0)
    }
    return sum
}

// 双指针优化
function trap(height) {
    // 对于每个位置来说。[当前位置的高度]和[左、右两侧最高高度]将决定当前位置可以存多少格水
    let sum = 0
    // 第一个和最后一个是边界,无法存水,所以直接跳过
    let leftIdx = 1
    let rightIdx = height.length - 2
    let leftMax = height[0]
    let rightMax = height[height.length - 1]
    while (leftIdx <= rightIdx) {
        // 对于左指针的元素来说,leftMax是绝对的。rightMax是不确定的
        // 对于右指针来说,rightMax是绝对的,leftMax是不确定的
        // 比如说:leftMax比rightMax小,那么对于左指针指向的元素来说,它左侧的上限已经被确定了。
        // 所以可以确定这个元素可以存多少水。同理,右指针指向的元素就是leftMax > rightMax的情况。
        // 计算出当前位置存水量后,更新max,指针移动
        if (leftMax <= rightMax) {
            sum += Math.max(leftMax - height[leftIdx], 0)
            leftMax = Math.max(leftMax, height[leftIdx])
            leftIdx++
        }
        if (rightMax < leftMax) {
            sum += Math.max(rightMax - height[rightIdx], 0)
            rightMax = Math.max(rightMax, height[rightIdx])
            rightIdx--
        }
    }
    return sum
}