跟着代码随想录练算法——队列和栈
- [232. 用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/)
- [225. 用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/)
- [1047. 删除字符串中的所有相邻重复项](https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/)
- [20. 有效的括号](https://leetcode.cn/problems/valid-parentheses/)
- [239. 滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum/)
- [150. 逆波兰表达式求值](https://leetcode.cn/problems/evaluate-reverse-polish-notation/)
- [347. 前 K 个高频元素](https://leetcode.cn/problems/top-k-frequent-elements/)
232. 用栈实现队列
一个栈用做输入栈,一个作为输出栈
- push 直接push在输入栈 inStack的末尾
- pop 先判断输出栈是否有元素,如果没有,需要将输入栈的元素一个个pop出来再push进输出栈,最后将输出栈pop并且返回
- peek 前面与pop一样,最终不需要pop输出栈的元素,只需要将该元素返回即可
- empty 判断两个栈,如果都为空才返回 true,否则返回false
var MyQueue = function() {
this.inStack = []
this.outStack = []
};
/**
* @param {number} x
* @return {void}
*/
MyQueue.prototype.push = function(x) {
this.inStack.push(x)
console.log('push',this.inStack,this.outStack)
};
/**
* @return {number}
*/
MyQueue.prototype.pop = function() {
//outStack 不为空
if(this.outStack.length === 0){
while(this.inStack.length){
this.outStack[this.outStack.length] = this.inStack.pop()
}
}
console.log('pop',this.inStack,this.outStack)
return this.outStack.pop()
};
/**
* @return {number}
*/
MyQueue.prototype.peek = function() {
if(this.outStack.length === 0){
while(this.inStack.length){
this.outStack[this.outStack.length] = this.inStack.pop()
}
}
console.log('peek',this.inStack,this.outStack)
return this.outStack[this.outStack.length - 1]
};
/**
* @return {boolean}
*/
MyQueue.prototype.empty = function() {
if(this.outStack.length === 0 && this.inStack.length === 0){
return true
}
console.log('empty',this.inStack,this.outStack)
return false
};
225. 用队列实现栈
一个队列用来放元素,一个用来临时保存
- push 直接push进队列1的末尾
- pop,top 先将队列1中的 this.queue1.length - 1 个元素放在queue2中,队列是先进先出,不会改变元素顺序,相当于直接复制过去,然后在将queue1中剩余的元素保存下来,再pop出去(top的话就不需要pop),再将queue2中的元素再复制到1里去,将刚刚保存的元素作为返回值,如果是top操作还需要把这个元素放回queue1
- empty 直接判断queue1是否为空
var MyStack = function() {
this.queue1 = []
this.queue2 = []
};
/**
* @param {number} x
* @return {void}
*/
MyStack.prototype.push = function(x) {
this.queue1.push(x)
};
/**
* @return {number}
*/
MyStack.prototype.pop = function() {
while(this.queue1.length - 1){
this.queue2.push(this.queue1.shift())
}
let item = this.queue1.pop()
while(this.queue2.length){
this.queue1.push(this.queue2.shift())
}
return item
};
/**
* @return {number}
*/
MyStack.prototype.top = function() {
while(this.queue1.length - 1){
this.queue2.push(this.queue1.shift())
}
let item = this.queue1.pop()
while(this.queue2.length){
this.queue1.push(this.queue2.shift())
}
this.queue1.push(item)
return item
};
/**
* @return {boolean}
*/
MyStack.prototype.empty = function() {
if(this.queue1.length === 0) return true
return false
};
1047. 删除字符串中的所有相邻重复项
/**
* @param {string} s
* @return {string}
*/
var removeDuplicates = function(s) {
if(!s || s.length === 0) return true
let stack = []
let arr = s.split('')
for(let i = 0; i < arr.length; i++){
if(stack.length === 0 || arr[i] !== stack[stack.length - 1]){
stack.push(arr[i])
}else{
stack.pop()
}
}
return stack.join('')
};
20. 有效的括号
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let obj = {
'{':'}',
'(':')',
'[':']'
}
let arr = s.split('')
let stack = []
for(let i = 0; i < arr.length; i++){
if(obj[arr[i]]){
stack.push(arr[i])
}else{
if(stack.length === 0) return false
let top = stack.pop()
if(obj[top] !== arr[i]) return false
}
}
if(stack.length !== 0) return false
return true
};
239. 滑动窗口最大值
维护一个单调递减的队列,队列的头表示当前区间的最大元素
- 指针ij指向窗口的头和尾
- 每次移动窗口需要取更新这个单调队列
- 如果从窗口中踢出去的元素nums[i-1]和队列的头部相等,则需要将队头元素出队,使用shift方法
- 然后还需要将新加入的窗口元素nums[j]与队列尾部元素比较,如果队尾元素比nums[j]要小则pop出去,直到队列为空或者队列最后一个元素大于等于nums[j],保证更新后队列依旧是单调的
- 更新完队列后,队列头元素也就是当前窗口最大值,放到ans中
- 最后j走到nums的最后一个元素,遍历结束,返回ans
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
let queue = [nums[0]]
if(nums.length === 1) return queue
for(let i = 1; i < k; i++){
while(queue.length && nums[i] > queue[queue.length - 1]){
queue.pop()
}
queue.push(nums[i])
}
let ans = [queue[0]]
let i = 1
let j = k
while(j < nums.length){
if(nums[i-1] === queue[0]){
queue.shift()
}
while(queue.length && nums[j] > queue[queue.length - 1]){
queue.pop()
}
queue.push(nums[j])
ans.push(queue[0])
i ++
j ++
}
return ans
};
150. 逆波兰表达式求值
- 遇到字符串就从栈中取出两个数做计算
- 注意除法运算,栈顶元素是除数,下面的那一个是被除数。并且除法是保留整数部分,也就是正数是向下取整,负数是向上取整
- 遇到数组就先将字符串转换为数字再放入栈中
/**
* @param {string[]} tokens
* @return {number}
*/
var evalRPN = function(tokens) {
let stack = []
for(let i = 0; i < tokens.length; i++){
if(tokens[i] === '+' || tokens[i] === '-' || tokens[i] === '*' || tokens[i] === '/'){
let a = stack.pop()
let b = stack.pop()
switch(tokens[i]){
case '+':
stack.push(b + a)
break
case '-':
stack.push(b - a)
break
case '*':
stack.push(b * a)
break
case '/':
if(b/a > 0)
stack.push(Math.floor(b/a))
else stack.push(Math.ceil(b/a))
break
}
}else{
stack.push(Number(tokens[i]))
}
}
return stack[0]
};
347. 前 K 个高频元素
思路:
- 使用map统计数字出现的频率,作为该元素的权值
- 遍历map中的元素,将key放入arr数组,维护一个大小为k的小顶堆:
- 当放元素进arr时发现小顶堆大小已经等于k,判断当前元素频率和堆顶元素评论,若比堆顶元素频率还要小则忽略,若大于堆顶元素频率,则现将堆尾元素弹出并且替代堆顶,然后向下调整,最后再放入当前元素,向上调整
- 如果最小堆还没有达到k,则只需要将元素放入,然后向上调整
- 如果最小堆内没有元素,则只需要将元素放入,不需要调整
- 有一个注意点就是当k=1 时,上一步的放入元素的第一小点,只需要弹出元素就可以了。
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
let map = new Map()
for(let i = 0; i < nums.length; i++){
if(map.has(nums[i])){
map.set(nums[i], map.get(nums[i]) + 1)
}else {
map.set(nums[i], 1)
}
}
let arr = []
let size = 0
function dubbleUp(index){
while(index){
let parentIndex = Math.floor((index - 1)/2)
if(map.get(arr[parentIndex]) > map.get(arr[index])){
[arr[parentIndex], arr[index]] = [arr[index], arr[parentIndex]]
index = parentIndex
}else{
break
}
}
}
function dubbleDown(index){
const lastIndex = arr.length - 1
while(index < lastIndex){
let findIndex = index
let leftIndex = index*2 + 1
let rightIndex = index*2 + 2
if(leftIndex <= lastIndex && map.get(arr[leftIndex]) < map.get(arr[findIndex])){
findIndex = leftIndex
}
if(rightIndex <= lastIndex && map.get(arr[rightIndex]) < map.get(arr[findIndex])){
findIndex = rightIndex
}
if(findIndex > index){
[arr[findIndex], arr[index]] = [arr[index], arr[findIndex]]
index = findIndex
}else{
break
}
}
}
map.forEach((val, key) => {
// console.log('val, key,size, arr:',val, key, size, arr)
if(size >= k){
// 如果当前元素比堆顶元素权值要大
if(map.get(key) > map.get(arr[0])){
if(k > 1){
arr[0] = arr.pop()
dubbleDown(0)
}else arr.pop()
arr.push(key)
dubbleUp(size - 1)
}
}else if(size === 0){
arr.push(key)
size ++
}else {
arr.push(key)
size ++
dubbleUp(size - 1)
}
})
return arr
};