代码随想录算法训练营day10|| 栈与队列基础、232.用栈实现队列、225. 用队列实现栈

栈(stack)

栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。

压栈:向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;

出栈:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈的工作原理:后进先出

操作:new(新建)push(入栈)pop(出栈)peek(获取栈顶元素)isEmpty(判断栈是否为空)

public static void main(String[] args) {
       Stack<Integer> s = new Stack();
       s.push(1);
       s.push(2);
       s.push(3);
       s.push(4);
       System.out.println(s.size()); // 获取栈中有效元素个数---> 4
       System.out.println(s.peek()); // 获取栈顶元素---> 4
       s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
       System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
       if(s.empty()){
           System.out.println("栈空");
       }else{
           System.out.println(s.size());
       }
}

队列(Queue)

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

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

队列的使用

***在Java中,Queue是个接口,底层是通过链表实现的。***

 注意: Queue 是个接口,在实例化时必须实例化 LinkedList 的对象,因为 LinkedList 实现了 Queue 接口。

public static void main(String[] args) {
       Queue<Integer> q=new LinkedList<>();
       q.offer(1);
       q.offer(2);
       q.offer(3);
       q.offer(4);
       q.offer(5);                  // 从队尾入队列
       System.out.println(q.size());
       System.out.println(q.peek());  // 获取队头元素
       q.poll();
       System.out.println(q.poll());  // 从队头出队列,并将删除的元素返回
       if(q.isEmpty()){
              System.out.println("队列空");    
       }else{
              System.out.println(q.size());    
       }
}

232.用栈实现队列

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

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

思路:

这是一道模拟题,不涉及到具体算法,考察的就是对栈和队列的掌握程度。

使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。

在push数据的时候,只要数据放进输入栈就好,

但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。

最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。

在代码实现的时候,会发现pop() 和 peek()两个函数功能类似,代码实现上也是类似的,可以思考一下如何把代码抽象一下。

class MyQueue {
    //声明一个名为stackIn的整型栈(用于入队操作),全局变量,只能放MyQueue外面
    Stack<Integer> stackIn;
    Stack<Integer> stackOut;

    public MyQueue() {
    //初始化队列
    stackIn = new Stack<>();//初始化
    stackOut = new Stack<>();//同上,用于出队
    }
    
    //将元素 x 入栈到 stackIn 中,实现了队列的入队操作
    public void push(int x) {
        stackIn.push(x);
    }
    //定义了一个出队方法 pop,用于移除队列头部的元素并返回。
    public int pop() {
        dumpstackIn();
        return stackOut.pop();//这时输出栈弹出来的,就是队列出队的头部元素
    }
    //定义了一个查看队列头部元素的方法 peek,不移除元素
//这里peek()的实现,直接复用了pop(),要不然,对stOut判空的逻辑又要重写一遍。技巧!效率!
    public int peek() {
        dumpstackIn(); //同pop过程,调用抽象出来的
        return stackOut.peek();
    }
    
    public boolean empty() {
        return stackIn.isEmpty() && stackOut.isEmpty();//stackIn和stackOut都为空
    }

    // 如果stackOut为空,那么将stackIn中的元素全部放到stackOut中
    public void dumpstackIn(){
        if(!stackOut.isEmpty()) return; //if stackOut不为空(为真)
        //否则,就是stackOut为空,需要将stackIn中的元素倒入stackOut
        while(!stackIn.isEmpty()){
            stackOut.push(stackIn.pop());//stackIn中的元素一直弹空为止
        }
    }
}

/**
 * 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();
 */
  • 时间复杂度: push和empty为O(1), pop和peek为O(n)
  • 空间复杂度: O(n)

扩展:

可以看出peek()的实现,直接复用了pop(), 要不然,对stOut判空的逻辑又要重写一遍。

再多说一些代码开发上的习惯问题,在工业级别代码开发中,最忌讳的就是 实现一个类似的函数,直接把代码粘过来改一改就完事了。

这样的项目代码会越来越乱,一定要懂得复用,功能相近的函数要抽象出来,不要大量的复制粘贴,很容易出问题!(踩过坑的人自然懂)

 225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

 思路:

队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。

队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。

所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。

但是依然还是要用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全用来备份的!

如下面动画所示,用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

import java.util.LinkedList;
import java.util.Queue;

class MyStack {
    // 定义两个队列que1和que2
    Queue<Integer> que1; 
    Queue<Integer> que2;  //备份队列

    //定义了MyStack类的构造方法,在这个方法里进行了初始化工作。
    public MyStack() {
        // 初始化两个队列
        que1 = new LinkedList<>(); //java中,Queue是个接口,底层是通过链表实现的
        que2 = new LinkedList<>(); //初始化 queue2 队列,使用了 LinkedList 类来实现
    }

    //定义了一个入栈方法 push,参数是要入栈的元素 x
    public void push(int x) { 
        // 将元素x推入que1队列
        que1.offer(x);
    }

    //定义了一个出栈方法pop,用于移除栈顶的元素并返回
    public int pop() {
        int size = que1.size();
        size--; // 减去1,因为我们要留最后一个元素在que1中

        // 将que1中的元素依次移到que2中,但要留下最后一个元素
        while (size-- > 0) {
            que2.offer(que1.poll());
        }

        // 留下的最后一个元素就是我们要返回的元素
        int result = que1.poll();
        
        // 将que2的元素移回que1
        while (!que2.isEmpty()) {
            que1.offer(que2.poll());
        }

        // 返回结果
        return result;
    }

    //定义了一个查看栈顶元素的方法 top,不移除元素。
    public int top() {
        int size = que1.size();
        size--; //size-1,保留队列末尾的值

        // 将que1中的元素依次移到que2中,但要留下最后一个元素
        while (size-- > 0) {
            que2.offer(que1.poll());
        }

        // 留下的最后一个元素就是栈顶元素
        int result = que1.peek();
        
        // 把最后一个元素也移到que2中
        que2.offer(que1.poll());
        
        // 将que2的元素移回que1
        while (!que2.isEmpty()) {
            que1.offer(que2.poll());
        }

        // 返回栈顶元素
        return result;
    }

    //栈 判空
    public boolean empty() {
        // 检查que1是否为空
        return que1.isEmpty();
    }
}

------ 其实这道题目就是用一个队列就够了。

一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。

class MyStack {
    Queue<Integer> queue; //声明了一个名为 queue 的整型队列。(全局变量)

    public MyStack() { //定义了MyStack类的构造方法,在这个方法里进行了初始化工作。
        queue = new LinkedList<>();//初始化 queue 队列,使用了LinkedList类来实现。
    }
    
    //定义了一个入栈方法 push,参数是要入栈的元素 x。
    public void push(int x) {
        queue.add(x);//将元素 x 入队到 queue 中,实现了栈的入栈操作。
    }
    //定义了一个出栈方法 pop,用于移除栈顶的元素并返回。
    public int pop() {
        rePosition();//调用了rePosition方法,将队列中的元素重新排列,以实现栈的出栈操作。
        return queue.poll(); //从队列中移除并返回队列的头部元素,实现了栈的出栈操作。
    }
    //定义了一个查看栈顶元素的方法 top,不移除元素。
    public int top() {
        rePosition();//同样调用了 rePosition 方法,确保队列中的元素重新排列(即现在尾变头了)
        int result = queue.poll(); //从队列中移除并记录栈顶的元素 记为result
        queue.add(result); // 将记录的栈顶元素重新入队到队列中
        return result; //返回记录的栈顶元素,实现了栈的取栈顶元素的操作
    }
    
    public boolean empty() {
        return queue.isEmpty();

    }
    //定义了一个重新排列队列元素的方法 rePosition
    public void rePosition(){
        int size = queue.size(); // 获取队列的大小
        size --;
        //将队列的大小减一,因为栈是后进先出的结构,所以队列的最后一个元素需要排在队列的头部
        //这么做可以保留最后一个元素
        while(size-- > 0){ //循环直到队列的大小减为 0
            queue.add(queue.poll());//将队列中的元素依次取出并重新加入队列,直到最后一个元素移至队列头部
        }
    }
}

/**
 * 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();
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值