Day10-D11-栈与队列,leetcode232,225,20,1047

栈与队列

理论

  1. 栈,先进后出
  • 栈方法

    • push,添加元素
    • pop,移除元素
    • peek,查看栈顶
    • isEmpty,检查空状态
    • size,获取大小
    • clear,清空
  • 由于栈结构的特殊性,非常适合做对称匹配类的题目。

// 基于数组实现队列
class Stack {
  constructor() {
    this.items = [];
  }
//   JavaScript 数组的 push() 和 pop() 方法时间复杂度都是 O(1)
  // 入栈
  push(element) {
    this.items.push(element);
  }
  
  // 出栈
  pop() {
    if (this.isEmpty()) return undefined;
    return this.items.pop();
  }
  
  // 查看栈顶元素
  peek() {
    if (this.isEmpty()) return undefined;
    return this.items[this.items.length - 1];
  }
  
  // 检查是否为空
  isEmpty() {
    return this.items.length === 0;
  }
  
  // 获取栈大小
  size() {
    return this.items.length;
  }
  
  // 清空栈
  clear() {
    this.items = [];
  }
}
  1. 队列,先进先出
  • 队列方法
    • enqueue,添加元素
    • dequeue,移除元素
    • front / peek,查看顶部
    • isEmpty,检查空状态
    • size,获取大小
    • clear,清空
// 基于数组实现,不推荐
class Queue {
  constructor() {
    this.items = [];
  }
  
  // 入队
  enqueue(element) {
    this.items.push(element);
  }
  
  // 出队
  dequeue() {
    if (this.isEmpty()) return undefined;
    return this.items.shift();
  }
  
  // 查看队首元素
  front() {
    if (this.isEmpty()) return undefined;
    return this.items[0];
  }
  
  // 检查是否为空
  isEmpty() {
    return this.items.length === 0;
  }
  
  // 获取队列大小
  size() {
    return this.items.length;
  }
  
  // 清空队列
  clear() {
    this.items = [];
  }
}
// 问题:shift() 操作的时间复杂度是 O(n),因为需要移动所有剩余元素

  • 优化的队列实现
// 方案1:使用对象和指针
class OptimizedQueue {
  constructor() {
    this.items = {};
    this.frontIndex = 0;
    this.rearIndex = 0;
  }
  
  enqueue(element) {
    this.items[this.rearIndex] = element;
    this.rearIndex++;
  }
  
  dequeue() {
    if (this.isEmpty()) return undefined;
    const item = this.items[this.frontIndex];
    delete this.items[this.frontIndex];
    this.frontIndex++;
    return item;
  }
  
  // 其他方法类似...
}
// 方案2:使用链表实现
class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

class LinkedListQueue {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }
  
  enqueue(value) {
    const newNode = new Node(value);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length++;
  }
  
  dequeue() {
    if (!this.head) return undefined;
    const value = this.head.value;
    this.head = this.head.next;
    this.length--;
    if (!this.head) this.tail = null;
    return value;
  }
  
  // 其他方法...
}


题目

  1. 用栈实现队列
  • 使用栈实现队列的下列操作:

  • push(x) -- 将一个元素放入队列的尾部。

  • pop() -- 从队列首部移除元素。

  • peek() -- 返回队列首部的元素。

  • empty() -- 返回队列是否为空。

  • 思路:

  • 需要利用两个栈,一个用来入栈,一个用来出栈,在push数据时,只要把数据放进输入栈就好,但在pop时,输出栈如果为空,就把进栈所有数据全部倒入进来到出栈,再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据。如果进栈和出栈都为空的话,说明模拟的队列为空了。

var MyQueue = function() {
    this.stackIn = []
    this.stackOut = []
}
MyQueue.prototype.push = function(x) {
    this.stackIn.push(x)
}

MyQueue.prototype.pop = function() {
    const size = this.stackOut.length
    if (size) {
        return this.stackOut.pop()
    }
    while(this.stackIn.length) {
        this.stackOut.push(this.stackIn.pop())
    }
    return this.stackOut.pop()
}

MyQueue.prototype.peek = function() {
    // 先调用 pop() 方法弹出队首元素(即模拟队列的第一个元素)。
    const x = this.pop()
    // 因为 peek 只是“查看”队首元素,不应该真的移除它,所以把刚刚弹出的元素又放回 stackOut 栈顶,保证队列状态不变。
    this.stackOut.push(x)
    // 返回队首元素的值。
    return x
}

MyQueue.empty = function() {
    return !this.stackIn.length && !this.stackOut.length
}


  1. 用队列实现栈
  • 使用队列实现栈的下列操作:

  • push(x) -- 元素 x 入栈

  • pop() -- 移除栈顶元素

  • top() -- 获取栈顶元素

  • empty() -- 返回栈是否为空

  • 思路

  • 队列先进先出,栈先进后出,同样123存放于队列和栈栈,栈会先弹出3,而队列会先弹出1,为了使队列实现栈的功能,即队列也弹出1,可以考虑队列长度len,然后先弹出len-1长度的数据,再重新加入队列,再弹出来,即是最后一个元素被弹出,

// 使用一个队列实现
var MyStack = function() {
    // 用一个数组 queue 作为队列,来模拟栈的操作
    this.queue = [];
};

MyStack.prototype.push = function(x) {
    // 直接把元素加入队列尾部。
    this.queue.push(x);
};

// 通过 while 循环,把队列前面的 size-1 个元素依次移到队尾。
// 这样原本最后加入的元素(栈顶)就被移到了队首。
// 最后 shift() 弹出队首元素,相当于弹出栈顶。
MyStack.prototype.pop = function() {
    let size = this.queue.length;
    while(size-- > 1) {
        this.queue.push(this.queue.shift());
    }
    return this.queue.shift();
};

// 先用 pop() 得到栈顶元素(并移除)。
// 再把这个元素重新放回队尾,保证队列状态不变。
// 返回这个元素。
MyStack.prototype.top = function() {
    const x = this.pop();
    this.queue.push(x);
    return x;
};

// 判断队列是否为空。
MyStack.prototype.empty = function() {
    return !this.queue.length;
};



  1. 有效的括号
  • 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

  • 有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。

  • 左括号必须以正确的顺序闭合。

  • 注意空字符串可被认为是有效字符串。

  • 思路:

  • 第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

  • 第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

  • 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

  • 字符串遍历完之后,栈是空的,就说明全都匹配了。

  • 遍历字符串,遇到左括号就往栈里放右括号(为了后面遍历到右括号时方便做比较),遇到右括号,要去栈里去匹配,找字符串前面是否出现对应的左括号,如果栈为空或者如果栈顶的元素不等于当前遍历的元素,说明不匹配。若遍历完字符串后,若栈不为空,即情况1,多做括号,返回false不匹配,

var isValid = function (s) {
  const stack = [];
  for (let i = 0; i < s.length; i++) {
    let c = s[i];
    switch (c) {
      case '(':
        stack.push(')');
        break;
      case '[':
        stack.push(']');
        break;
      case '{':
        stack.push('}');
        break;
      default:
        if (c !== stack.pop()) {
          return false;
        }
    }
  }
  return stack.length === 0;
};


var isValid = function(s) {
    const stack = [], 
        map = {
            "(":")",
            "{":"}",
            "[":"]"
        };
    for(const x of s) {
        if(x in map) {
            stack.push(x);
            continue;
        };
        if(map[stack.pop()] !== x) return false;
    }
    return !stack.length;
};


  1. 删除字符串中的所有相邻重复项
  • 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

  • 在 S 上反复执行重复项删除操作,直到无法继续删除。

  • 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

  • 思路:

  • 在删除相邻重复项的时候,需要知道当前遍历的这个元素,在前一位是不是遍历过一样数值的元素,那么就需要记录前面遍历过的元素。

  • 可以用栈来存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。然后再去做对应的消除操作。从栈中弹出剩余元素

  • 遍历完后,栈中的元素顺序就是最终结果的顺序(因为每次都是从左到右处理,栈底是字符串开头,栈顶是结尾)。

// 使用栈
var removeDuplications = function(s) {
    const result = []
    for (const val of s) {
        if (val === result[result.length - 1]) {
            // 如果当前字符和栈顶元素相同,则弹出栈顶(消除一对重复项)
            result.pop()
        } else {
            // 否则入栈
            result.push(val)
        }
    }
    // 栈中剩余元素即为最终结果
    return result.join('')
}



参考&感谢各路大神

posted @ 2025-06-06 16:58  安静的嘶吼  阅读(3)  评论(0)    收藏  举报