问:
- 实现一个特殊的栈,在实现栈基本功能基础上,再实现返回栈中最小元素的功能。要求:push、pop、getMin的时间复杂度都是O(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数据弹到栈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
}