栈与队列相关题


title: 栈和队列结合leetcode
categories: 数据结构与算法
tag:
- leetcode
- 栈和队列
date: 2021-09-16 20:26:34

栈与队列

栈和队列的描述

  1. 栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

    栈后进先出

  2. 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

  3. 队列先进先出

1. 用栈实现队列

  1. leetCode 232 用栈实现队列
思路

队列是先进先出,栈是先进后出,我们要使用两个栈来模拟队列的特性

当出队栈存在内容时,出队栈的栈顶,即为第一个出队的元素。

若出队栈无元素,我们的需求又是出队的话,我们就需要将入队栈的内容反序导入出队栈,然后弹出栈顶即可。

var MyQueue = function() {
    this.is = [];
    this.os = [];
};

MyQueue.prototype.push = function(x) {
    this.is.push(x);
};

MyQueue.prototype.pop = function() {
    if(!this.os.length){
        while(this.is.length){
            this.os.push(this.is.pop());
        }
    }
    return this.os.pop();
};

MyQueue.prototype.peek = function() {
    if(!this.os.length){
        while(this.is.length){
            this.os.push(this.is.pop());
        }
    }
    return this.os[this.os.length - 1];
};

MyQueue.prototype.empty = function() {
    return !this.is.length && !this.os.length;
};

2. 用队列实现栈

  1. leetCode225 用队列实现栈
// 使用一个队列实现
/**
 * Initialize your data structure here.
 */
var MyStack = function() {
    this.queue = []
}

/**
 * Push element x onto stack.
 * @param {number} x
 * @return {void}
 */
MyStack.prototype.push = function(x) {
    this.queue.push(x)
}

/**
 * Removes the element on top of the stack and returns that element.
 * @return {number}
 */
MyStack.prototype.pop = function() {
    let size = this.queue.length
    while (size-- > 1) {
        this.queue.push(this.queue.shift())
    }
    return this.queue.shift()
}

/**
 * Get the top element.
 * @return {number}
 */
MyStack.prototype.top = function() {
    const x = this.pop()
    this.queue.push(x)
    return x
}

/**
 * Returns whether the stack is empty.
 * @return {boolean}
 */
MyStack.prototype.empty = function() {
    return !this.queue.length
}

3. 有效的括号

  1. leetCode 20 有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

思路
  1. 遍历字符串,然后把他们放进一个栈中,遇到左括号类型就入栈,遇到有括号类型就将它与栈顶元素作匹配,若匹配成功,继续遍历。
  2. 遍历结束后判断栈的长度是否为0,若为0,就返回true.
const isValid = function(s) {
    let stack = []
    for (let i = 0; i < s.length; i++) {
        let c = s[i]
        switch (c) {
            case '(':
            case '[':
            case '{':
                stack.push(c)
                break
            case '}':
                if (stack.pop() !== '{') return false
                break
            case ']':
                if (stack.pop() !== '[') return false
                break
            case ')':
                if (stack.pop() !== '(') return false
                break
        }
    }
    return stack.length === 0
}

4. 删除字符串中所有相邻的重复项

  1. leetCode 1047 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。

思路

与上题思路类似,每次进栈前,与栈顶元素作对比,如果与栈顶元素相同,就出栈,不同就进栈。

/**
 * @param {string} s
 * @return {string}
 */
var removeDuplicates = function(S) {
    let stack = []
    for (let item of S) {
        if (stack[stack.length - 1] === item) {
            stack.pop()
        } else {
            stack.push(item)
        }
    }
    return stack.join('')
}
console.log(removeDuplicates('abbaca'))

5. 逆波兰表达式求值

  1. leetCode 105 逆波兰表达式求值

有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

思路
  1. 首先定义一个hash表,做加减乘除和数字的对应关系
  2. 然后遍历数组,如果在hash表中不存在这个。(也就是说,是数字),就push进栈,然后跳出这轮循环
  3. 如果是加减乘除的符号。就进行运算。
  4. 最后返回运算后的结果
/**
 * @param {string[]} tokens
 * @return {number}
 */
var evalRPN = function(tokens) {
    const s = new Map([
        ["+", (a, b) => a * 1  + b * 1],
        ["-", (a, b) => b - a],
        ["*", (a, b) => b * a],
        ["/", (a, b) => (b / a) | 0]
    ]);
    const stack = [];
    for (const i of tokens) {
        if(!s.has(i)) {
            stack.push(i);
            continue;
        }
        stack.push(s.get(i)(stack.pop(),stack.pop()))
    }
    return stack.pop();
};

js除了Math.floor方法,还可以通过位运算|,>>实现向下取整。

x是必须的,并且是一个数值

Math.floor(x)
console.log(Math.floor(0.8));//输出0
console.log(Math.floor(-0.8));//输出-1
console.log(Math.floor(1));//输出1
console.log(Math.floor('d'));//如果不是数值,输出NaN
位运算符实现向下取整
console.log(0.8 | 0);//输出0
console.log(-0.8 | 0);//输出0
console.log(1 | 0);//输出1

或者使用>>

console.log(0.8 >> 0);//输出0
console.log(-0.8 >> 0);//输出0
console.log(1 >> 0);//输出1
位运算和Math.floor()的差别
var a=(Math.pow( 2,32)/2-1)+0.5;//a=2147483647.5
a | 0;
a >> 0;
Math.floor(a);
//结果都为2147483647
a+=1;
a | 0;//输出-2147483648
a >> 0;//输出-2147483648
Math.floor(a);//输出2147483648

当值为负数的时候。位运算和Math.floor不一致

//只是由于它先进行转为32位的整数,再在进行舍去小数位,最后转变为负数的结果。
console.log(Math.floor(-0.8));//输出-1  
console.log(-0.8 | 0);//输出0  
console.log(-0.8 >> 0);//输出0

6. 滑动窗口最大值

  1. leetCode 239 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
思路

解法一:遍历,push进窗口,然后当长度达到k,就删除第一个元素,再push进去。会超时

var maxSlidingWindow = function(nums, k) {
    let result = []
    let window = []
    for (let i = 0; i < nums.length + 1; i++) {
        if (window.length == k) {
            result.push(Math.max(...window))
            window.shift()
        }
        window.push(nums[i])
    }
    return result
}
console.log(maxSlidingWindow([1, -1], 1))

解法二:

7. 前K个高频元素

  1. leetCode 347前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

思路

使用一个hash表来存储出现的次数,然后从大到小排序,再然后前k个

var topKFrequent = function(nums, k) {
    let map = new Map()
    let arr = [...new Set(nums)]
    for (let i = 0; i < nums.length; i++) {
        map.set(nums[i], map.has(nums[i]) ? map.get(nums[i]) + 1 : 1)
    }
    let res = []
    res = arr.sort((a, b) => map.get(b) - map.get(a)).slice(0, k)
    return res
}
let result = topKFrequent([1, 1, 1, 2, 2, 3], 2)
console.log(result)

参考

代码随想录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值