当前位置: 首页 > article >正文

Day10补代码随想录 理论基础|232.用栈实现队列|225.用队列实现栈|20.有效的括号|1047.删除字符串中的所有相邻重复项

栈和队列理论基础

抽象认识

  • 栈是先进后出(FIFO),队列是先进先出(LIFO)
    • 队首(先进))队尾(后进)
    • 栈顶(后进)栈底(先进)
  • 栈(Stack)
    • 只在一端进行进出操作(只在一端进一端出)
    • 像个篮球框,取用篮球从一端进出。
    • /进栈
      int a[1000];//足够大的栈空间
      int top=-1;//栈的初始化,栈顶位置标记
      for(i=0;i<10;i++){
      	a[++top]=i;//先对top+1,再录入位置,使用++top为了防止最后一次循环赋值后top依旧进行+1,使得top指向了栈外的空数组。
      }
      /出栈
      for(i=0;i<10;i++){
      print("%d",a[top--]);}
      
  • 队列(Queue)
    • 在两端分别进行进和出的操作(一端进一端出)
    • 食堂排队,从后面排队,前面买饭离开。
    • //QQ号,有10个数字的排列,排序为奇数的数字取出排列;排序为偶数的数字放入队尾重新报号,组成QQ号。
      int a[100]={1,2,3,4,5,6,7,8,9,0},head,tail;
      //队头队尾
      head=0;
      tail=10;//标记的是最后一个元素后面的空元素
      while(head<tail){
      	printf("%d",a[head]);//奇数离开队伍
      	head++;
      	a[tail]=a[head];//偶数放在队尾
      	tail++;//补长队尾
      	head++;//减少队头
      
      }
      
      

JAVA中的栈和队列

  • 栈(Stack) Stack类位于java.util包中
    • 基本操作
      • push(E item):将元素压入栈顶。
      • pop():移除并返回栈顶的元素。
      • peek():查看栈顶的元素,但不移除。
      • isEmpty():判断栈是否为空。
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    • 低层实现:基于一个动态数组(Vector)来实现的,和C++类似,可以通过扩展Vector来管理栈中的元素。
  • 队列(Queue) Queue接口
    • 常见的实现类
      • LinkedList:提供队列的标准实现
      • PriorityQueue:优先队列,根据元素的优先级顺序出队。
      • ArrayDeque:基于数组的双端队列。
    • 基础操作
      • add(E e):将元素添加到队尾
      • remove():移除并返回队首的元素
      • peek():查看队首的元素,但不移除。
      • isEmpty():判断队列是否为空
    • 底层实现:默认的队列实现是LinkedList,基于双向链表来实现队列的入队和出队操作。ArrayDeque是基于数组的实现,通常性能更好。

几个问题

  • Java中的Stack是容器吗?
    • 是,Stack是一个继承自vector类的容器。vector是一个可动态扩展的数组,实现了List接口。Stack类本身是一个容器类,提供了栈(LIFO)操作,由于 Stack 继承自 Vector,所以它本质上也继承了 Vector 的所有特性,包括动态调整大小和随机访问等。然而,Stack 主要用来模拟栈的行为,提供了栈特有的操作,但并不暴露所有 Vector 提供的操作。
  • 我们使用的stack是属于什么?
    • JAVA标准库(JDK),位于java.util,
    • stack类是继承自Vector的一个类,设计用于支持栈(LIFO)行为。
  • JAVA的stack是如何实现的?
    • Stack是通过继承Vector类来实现的。Vector是一个动态数组,可以自动扩展它的容量。
    • Stack通过push()和pop()方法来操作Vector中的元素,以实现栈的LIFO行为。
  • stack提供迭代器来遍历stack空间吗?
    • 是的,但不推荐使用。
    • Stack提供了iterator()方法来获取一个迭代器,从而遍历栈中的元素、但是不符合栈的设计哲学,因为栈是符合先进后出原则,允许迭代器遍历栈中 元素会破坏这一原则。
    • 由于栈的特性,只允许访问栈顶元素,允许迭代的做法违背了栈的设计原则,因此并不推荐使用Stack类,推荐使用Deque(双端队列)或ArrayDeque来代替Stack,这些类支持栈和队列的操作,并且不会提供队元素的遍历行为。
  • 在java中,没有缺省容器的概念,栈类和队列类都是固定的,不能更换低层容易,Java中的Stack类的底层实现是固定的。

232.用栈实现队列

题目

https://leetcode.cn/problems/implement-queue-using-stacks/description/

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

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

  • 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

思路

  • 思路

    • 用****2个栈(入栈)****来改变出栈顺序
      • 入栈要全部放在出栈里,模拟队列的行为(如果出栈是空的)
      • 队列是先进先出,栈是先进后出,那么就让数先入栈,再将入栈的数移动到出栈里,开口不变。
    • 如果要弹出,看看出栈是否为空,如果为空,把所有元素加倒出栈里。
  • 代码思路

    • 写清各个接口的返回类型
    • 初始化入栈和出栈类,创建入栈和出战的对象。
    • class MyQueue{
          //声明入栈和出栈
          Stack<Integer> stackIn;
          Stack<Integer> stackOut;
      
          //Initialize 
          public MyQueue(){
              stackIn=new Stack<>();
              stackOut=new Stack<>();
      
          }
          //将一个元素放入队列的尾部
          public void push(int x){
              stackIn.push(x);//队列从入栈进入
      
          }
          //从队列首部移除元素
          public int pop(){//队列中的先进先出,就是取出一个元素并返回它。
              dumpstackIn();//在出栈放入栈pop出来的数
              return stackOut.pop();//弹出(移除)再返回值
      
          }
          //返回队列首部的元素
          public int peek(){
              dumpstackIn();
              return stackOut.peek();//返回栈顶,但不移除它。
      
          }
          //返回队列是否为空
          public boolean empty(){
              return stackIn.isEmpty()&&stackOut.isEmpty();
      
          }
      
          private void dumpstackIn(){
              if(!stackOut.isEmpty()) return;//如果出栈不是空的,那就返回不用执行
              while(!stackIn.isEmpty()){
                  stackOut.push(stackIn.pop());//放入栈pop出来的数
              }
          }
      }
      
      /**
       * Your MyQueue object will be instantiated and called as such:
       * MyQueue obj = new MyQueue();
       * obj.push(x);
       * int param_2 = obj.pop();
       * int param_3 = obj.peek();
       * boolean param_4 = obj.empty();
       */
      

总结

  • 声明入栈和出栈
    Stack <Integer> stackIn;
    Stack <Integer> stackOut;
  • 创建入栈和出栈的对象,stackIn=new Stack<>();stackOut=new Stack<>();
  • return stackOut.peek();//默认返回栈顶,但不移除它

225.用队列实现栈

题目

https://leetcode.cn/problems/implement-stack-using-queues/description/

https://programmercarl.com/0225.%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.html

【惯性思维用两个队列来模拟栈,其实只用一个队列就可以模拟栈了】

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

  • push(x) – 元素 x 入栈
  • pop() – 移除栈顶元素
  • top() – 获取栈顶元素
  • empty() – 返回栈是否为空

注意:

  • 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
  • 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)
  • 【单向队列】

思路

  • 我的思路
    • 队列是先进先出,我们让它先进后出,改变出口方向,每当队列满了之后,从列尾出去
  • Karl思路
    • 队列是先进先出的顺序,如果用两个队列,将一个队列移动到另一个队列中时,先进先出的顺序还是没有改变。
    • 栈pop的是栈顶元素(后进),队列pop的是队首,我们让栈顶元素(后进)元素pop到栈尾,假设栈有n个元素,每次让栈的栈底元素pop出来,需要将前n-1个元素放入到栈底后面。
  • 代码
    class MyStack {
        Queue<Integer> queue;//Queue是个接口
       
    
        public MyStack() {
            queue=new LinkedList<>();//LinkedList是Queue的一个实现类   
        }
      
        public void push(int x) {
            queue.add(x);//将元素添加到队尾,相当于栈整体移动到队列了   
        }
      
        public int pop() {
           rePosition();
           return queue.poll();//移除并返回栈首元素
    
    
          
        }
        public int top() {
            rePosition();
            int result=queue.poll();//先移除队首(实际是之前的队尾,即后进)
            queue.add(result);//后进在栈顶,返回
            return result;
          
        }
      
        public boolean empty() {
            return queue.isEmpty();
          
        }
        //将队列前面的元素加入到队列末尾
        public void rePosition(){
            int size=queue.size();
            size--;//需要将size-1个元素移动到队尾
            while(size-->0)//在每次循环后,将size-1,只要size>0就继续
                queue.add(queue.poll());//移除并返回队首元素,知道前size-1个元素全部返回
        }
    }
    
    /**
     * Your MyStack object will be instantiated and called as such:
     * MyStack obj = new MyStack();
     * obj.push(x);
     * int param_2 = obj.pop();
     * int param_3 = obj.top();
     * boolean param_4 = obj.empty();
     */
    

总结

  • size()和length的使用范围
    • size()是方法,length是属性
    • size():用于动态大小的集合类;Map的键值对数量
    • length:固定长度的数组,字符串
  • 注意事项
    • 接口和实现类

      • Queue <Integer> queue;//Queue是个接口
      • queue=new LinkedList<>();//LinkedList是Queue的一个实现类
    • queue的一些用法

      • queue.add(x) 将元素加入队尾
      • queue.poll() 移除并返回队首元素
    • top()方法需要多练习,很绕。

20.有效的括号

题目

https://programmercarl.com/0020.%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7.html

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

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。

示例 1:

  • 输入: “()”
  • 输出: true

示例 2:

  • 输入: “()[]{}”
  • 输出: true

示例 3:

  • 输入: “(]”
  • 输出: false

示例 4:

  • 输入: “([)]”
  • 输出: false

示例 5:

  • 输入: “{[]}”
  • 输出: true

思路

  • 我的思路
    • 思路很混乱,怎么能在栈和队列中实现这些
  • Carl
    • 三种不匹配的情况
      • 字符串里的左方向的括号多余了,不匹配。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 括号没有多余,但是括号的类型没有匹配上。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 右方向的括号多余了,不匹配
  • 代码思路
    • 如果是奇数,一定可以发现不匹配字符。
    • for循环
      • if 遇到(,在栈中加)
      • 提前准备比较匹配
        • else if { ,在栈中加}
        • elseif [,在栈中加]
      • 【情况2,3】elseif 栈空(情况3)||不匹配(情况2) return false;相等情况弹出。
      • 【情况1】 遍历到最后一个了,如果不是空栈,return false;
  • 代码
    class Solution {
        public boolean isValid(String s) {
            Deque<Character> deque=new LinkedList<>();//声明并创建实例
            char ch;
            for(int i=0;i<s.length();i++){
                ch=s.charAt(i);
                //提前匹配
                if(ch=='('){
                    deque.push(')');
                }else if(ch=='{'){
                    deque.push('}');
                }else if(ch=='['){
                    deque.push(']');
                }else if(deque.isEmpty()||deque.peek()!=ch){ //情况2,3
                    return false;
                }else{//如果是右括号判断是否和栈顶元素匹配
                    deque.pop();
                }
    
            }
            return deque.isEmpty();
    
    
        }
    }
    

总结

  • string要转换为char,才能比较
  • 注意,情况2,3先判断栈空【情况3】

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

题目

https://programmercarl.com/1047.%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%E7%9B%B8%E9%82%BB%E9%87%8D%E5%A4%8D%E9%A1%B9.html

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

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

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

示例:

  • 输入:“abbaca”
  • 输出:“ca”
  • 解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

提示:

  • 1 <= S.length <= 20000
  • S 仅由小写英文字母组成。

【栈的经典应用,栈喜欢做这种消除的操作,栈帮助记录了遍历数组当前元素的时候,前一个元素是什么】

思路

  • 我的思路
    • 难点,删除完bb后aa又靠近了。迭代。
    • 每遍历一个元素,
      • 如果有前一个元素,把他的前一个元素储push在栈中。

        • 比较它和前一个元素,如果匹配,就删除字符串里的这个元素和前一个元素,不匹配就pop。
      • else if

        • continue.
  • Carl思路
    • 消消乐,
      • if 栈是空的||不匹配,那么放入当前元素
      • else if 消除 pop弹出
      • 最后要把栈中的字符串翻转过来
  • 代码
    class Solution {
        public String removeDuplicates(String s) {
            ArrayDeque<Character> deque=new ArrayDeque<>();
            char ch;
            for(int i=0;i<s.length();i++){
                ch=s.charAt(i);
                //如果栈空或不匹配栈顶
                if(deque.isEmpty()||deque.peek()!=ch){
                    deque.push(ch);
                }else{//匹配就删掉
                    deque.pop();
                }
            }
            String str="";
            //剩余的元素
            while(!deque.isEmpty()){//全pop出去
                str=deque.pop()+str;//先pop出去的放在前面,恢复正序
            }
            return str;
    
    
        }
    }
    

总结

  • ArrayDeque 是JAVA中的双端队列数据结构,既可以用作栈,也可以用做队列

      • push()
      • pop()
    • 队列
      • add()
      • poll()
    //ArrayDeque会比LinkedList在除了删除元素这一点外会快一点
            //参考:https://stackoverflow.com/questions/6163166/why-is-arraydeque-better-than-linkedlist
    
  • 低级错误,ch=s.charAt(i); 里面是string s 的第i个,而不是直接ch=s.charAt(s);


http://www.kler.cn/a/459088.html

相关文章:

  • SD下载、安装、使用、卸载-Stable Diffusion整合包v4.10发布!
  • node.js之---CommonJS 模块
  • pandas-栗子
  • java实验4 反射机制
  • Spring Cloud Security集成JWT 快速入门Demo
  • RK3588+FPGA全国产异步LED显示屏控制卡/屏幕拼接解决方案
  • 工业串行总线中的“安全守护者”,隔离接口芯片
  • 「Mac畅玩鸿蒙与硬件49」UI互动应用篇26 - 数字填色游戏
  • Mysql数据库Redo日志和Undo日志的理解
  • wx011基于springboot+vue+uniapp的机电公司管理信息系统
  • FFmpeg 中 examples 使用教程
  • 软件需求分析期末知识点整理
  • 开启家具组装新方式:产品说明书智能指导
  • CSS系列(36)-- Containment详解
  • Odoo17 4模型安全访问控制:深入理解 model_id:id 和 group_id:id
  • LabVIEW 中 NI Vision 模块的IMAQ Create VI
  • [Excel] CONCATENATE TEXT
  • 实际部署Dify可能遇到的问题:忘记密码、开启HTTPS、知识库文档上传的大小限制和数量限制
  • 【Golang 面试题】每日 3 题(十一)
  • 爬虫基础之爬取 某漫画网站
  • 前端Python应用指南(七)使用SQLAlchemy与Django ORM:数据库操作的Python实践
  • 大数据-264 实时数仓 - Canal MySQL的binlog研究 存储目录 变动信息 配置MySQL
  • 论文笔记PhotoReg: Photometrically Registering 3D Gaussian Splatting Models
  • 【Unity功能集】TextureShop纹理工坊(七)魔棒工具
  • 深入浅出:从入门到精通大模型Prompt、SFT、RAG、Infer、Deploy、Agent
  • JavaFX与Gradle版本兼容指南