栈与队列(Java中的栈和队列如何定义)

        刷了两题栈和队列的力扣后发现,难点不在算法,但是对Java中对栈和队列的定义好混乱,会涉及到Stack/Queue/Deque/ArrayDeque/LinkedList,我们到底要怎么选,以及他们分别有什么函数,add/offer/push/pop/poll/peek这几个函数到底该什么时候用。对于第一次用Java写栈和队列的新手来说,确实很容易搞糊涂。所以我参考的网上的各种资料和jdk11开发者文档对这些类和接口的说明,把他们一起整理一下。

        下面我就会分别介绍Stack/Queue/Deque/ArrayDeque/LinkedList这个五个的作用和常用函数,以及他们直接的关系,最后会说明力扣里面我们要如何选择和使用。

        说明:下面的介绍轻底层原理,简单介绍完理论之后,更多的是强调如何使用。

目录

一.Stack

1.简单说明

2.Stack的定义

3.Stack的函数调用

4.使用举例

二.Queue

1.简单说明

2.Queue的定义

3.Queue的函数调用

4.使用举例 

三.Deque

1.简单说明

2.Deque的定义

3.Deque中关于栈的函数

4.Deque中关于队列的函数

四.总结

1.Stack栈的使用

2.Queue队列的使用

 3.Deque双端队列的使用


一.Stack

1.简单说明

        Stack是一个普通类,继承了Vector接口,增加了五个操作,push、pop、peek、empty和search方法。

2.Stack的定义

        栈的构造方法只有一个,如下图。

         因此,如果我们要构造一个Stack对象,要怎么写呢?

        下面我构造了一个空栈,栈内的元素分别是是Integer和Character包装类。其他类型的栈格式是一样的。

//力扣里上下两种都能用
Stack<Integer> stack = new Stack<>();
Stack<Character> stack = new Stack<>();

//IJ里面只能用下面这种,<>内部要写东西
Stack<Integer> stack = new Stack<Integer>();
Stack<Character> stack = new Stack<Character>();

        我也在我的IJ里面试了,大家可以看图。<>里面不写元素类型,会直接报错。

3.Stack的函数调用

        Stack的全部方法如下,只有5个。而我们在刷力扣是常用的是前4个。

只要会下面几个函数,在力扣上用Stack实现栈就没问题了。

(1)empty():判断stack是否为空,如果为空返回true,不为空返回false。

        isEmpty():和empty功能一样,且这两个判空的函数在力扣里都能编译通过,大家可以随便选来用。

        那为什么Stack类里面isEmpty方法但是我们可以使用呢,因此isEmpty方法是封装在Collection里的,而Stack类是Collection的子类,所有可以用上层父类的方法。(Collection->List->Vector->Stack这个是由父到子的继承的关系)

(2)peek():获取栈顶的元素

(3)pop():获取栈顶的元素,并将其删除

(4)push(x):在栈顶添加元素x

(5)size():获取当前栈的长度(是Collection父类中继承过来的)

4.使用举例

        下面的代码是力扣232.用栈实现队列的题目,包括的栈的定义,push、pop以及判空操作。这里的判空用empty()和isEmpty()都可以。

class MyQueue {
 
    Stack<Integer> in;
    Stack<Integer> out;
    public MyQueue() {
        in = new Stack<>();
        out = new Stack<>();
 
    }
    
    public void push(int x) {
        in.push(x);  //输入栈模拟队列的输入
    }
    
    public int pop() {
        if(out.isEmpty()){   //输出栈模拟队列的输出
            while(!in.isEmpty()){  //如果输入栈为空,把输入栈元素全部移入输出栈
                out.push(in.pop());  
            }
        }
        int result = out.pop();
        return result;
    }
    
    public int peek() {
        int result = this.pop(); //函数复用,调用上面的pop
        out.push(result);  //把pop的元素push回去
        return result;
    }
    
    public boolean empty() {
        return in.isEmpty() && out.isEmpty();
    }
}

二.Queue

1.简单说明

        Queue是一个接口,继承了Collection接口。

2.Queue的定义

        因为Queue是一个接口,所有没有构造方法。因此在定义的时候,右边new的对象必须是Queue接口的实现类,这里有两个类LinkedList和ArrayDeque可以选择。下面我给了Queue接口定义的示例代码。

//这一种只能在力扣里用,IJ里会报错
Queue<Integer> queue = new LinkedList<>();
Queue<Integer> queue = new ArrayDeque<>();


//这一种在力扣和IJ里都能用
Queue<Integer> queue = new LinkedList<Integer>();
Queue<Integer> queue = new ArrayDeque<Integer>();

        先说说结论,如果要在力扣上通过,LinkedList和ArrayDeque其实都能用,性能上也没什么差距,大家不用纠结。如果非要选,ArrayDeque更好,因为它在性能和内存使用方面通常更优越,而且力扣上队列的题基本都是在队列的头尾操作。

        下面简单介绍一下两者的区别。

(1)LinkedList和ArrayDeque是什么?

         他俩都是实现了Queue接口的实现类。

(2) LinkedList和ArrayDeque有什么区别?

  LinkedList是一个基于链表的数据结构,它使用双向链表来存储元素。每个元素在内存中不是连续存储的,而是通过指针(或引用)连接在一起。

        ArrayDeque是一个基于数组的双端队列,它使用循环数组来存储元素。这意味着当达到数组的末尾时,它会回到数组的开头继续存储。

        其实简单来说,LinkedList底层用的是链表实现了双向队列,ArrayDeque的底层用的是数组实现了双向队列,原理一样,只是实现时用的数据结构不一样罢了。

(3)LinkedList和ArrayDeque我怎么选择使用?

         ArrayDeque优点就是内存少,主要针对在队列的头部和尾部操作。 LinkedList的优点就是方便在任意位置插入元素。

3.Queue的函数调用

        Queue的全部方法如下,我们在力扣上常用的其实也只有add、offer、peek和poll。

只要会下面几个函数,在力扣上用Queue实现队列就没问题了。

(1)poll():删除并获取队头元素

(2)peek():获取队尾元素

(3)add():在队尾添加元素

         offer():在队尾添加元素

add和offer都能在队列尾部添加元素,那两者的区别是什么呢?

        add :继承Collection接口。当队列已满时,add方法会抛出IllegalStateException。

        offer :是Queue接口特有的。当队列已满时,offer方法不会抛出异常,会返回false。

add和offer如何选择?

        其实在力扣里用哪个都行。

        但在日常实际开发中,如果希望在队列满时立即抛出异常,且代码能够处理这种异常情况,那么可以使用add方法。如果你不希望因为队列满而抛出异常,而是希望检查添加操作是否成功,并据此做出进一步的逻辑判断或处理,那么应该使用offer方法。

        在实际应用中,offer方法通常更受欢迎,因为它提供了更大的灵活性,允许调用者在不中断程序执行的情况下处理添加操作的失败情况。

(4)isEmpty():判断队列是否为,是空则返回true(是Collection父类中继承过来的)

(5)size():获取当前队列的长度(是Collection父类中继承过来的)

4.使用举例 

        下面的代码是力扣225.用队列实现栈的题目,包括的队列的定义,add或offer、push、poll、size以及判空操作,这里的判空用empty()和isEmpty()都可以。

class MyStack {
 
    Queue<Integer> que;
 
    public MyStack() {
        que = new LinkedList<>(); //双向链表
    }
    
    public void push(int x) {
        que.add(x);  //add添加到列表末尾,模拟栈的输入
    }
    
    public int pop() {
        tackle(); //如果队列元素大于1,要把前面的元素依次移到队尾
        int result = que.poll();  //poll用于删除队头元素
        return result;
    }
    
    public int top() {
        //以123为例,要想获取队尾元素3,先移动成312,把队尾元素3给poll,变成12,获取到3
        //再把3重新add到队尾,变成123,恢复初始状态
        tackle(); //如果队列元素大于1,要把前面的元素依次移到队尾
        int result = que.poll(); //poll删除队头元素,并返回队头元素
        que.add(result);  //把删除的元素重新加到队尾,
        return result;
    }
    
    public boolean empty() {
        return que.isEmpty();
    }
 
    //如果队列元素大于1,要把前面的元素依次移到队尾
    public void tackle(){
        int size = que.size();
        while(size-- > 1){
            que.add(que.poll());
        }
    }
}
 

三.Deque

1.简单说明

        Deque是一个接口,继承的了Queue接口,Deque定义了访问双端队列两端元素的方法。且Deque接口可以帮助我们同时实现栈或者队列。简单来说,如果我们要实现一个栈or一个队列,Deque是功能最强大的。

2.Deque的定义

        和Queue类似,Deque是继承了Queue的接口,因此在定义时,右边必须是实现类。

//这一种只能在力扣里用,IJ里会报错
Deque<Integer> queue = new LinkedList<>();
Deque<Integer> queue = new ArrayDeque<>();


//这一种在力扣和IJ里都能用
Deque<Integer> queue = new LinkedList<Integer>();
Deque<Integer> queue = new ArrayDeque<Integer>();

Deque接口提供的方法很多,那我们怎么用呢?

3.Deque中关于栈的函数

(1)如何在栈顶添加元素

push:用于在栈顶添加元素。(Deque额外加的)

记住push就行,addFisrt和offerFisrt只是实现功能一样,但是没必要用,容易搞乱

addFisrt:可以在栈顶添加元素(Deque额外加的)

offerFisrt:可以在栈顶添加元素(Deque额外加的)

(2)如何在栈顶弹出元素

pop:用于在栈顶弹出元素。(Deque额外加的)

记住pop就行,pollFisrt和removeFirst只是实现功能一样,但是没必要用,容易搞乱

pollFisrt:用于在栈顶弹出元素。(Deque额外加的)

removeFirst:可以获取并删除在队头的元素(Deque额外加的)

(3)如何获取队列头部的元素

peek:可以获取栈顶的元素(Queue中继承来的)

peekFirst:可以获取栈顶的元素(Deque额外加的)

getFirst:可以获取栈顶的元素(Deque额外加的)

记住peek就行,peekFirst和getFirst只是实现功能一样,但是没必要用,容易搞乱

4.Deque中关于队列的函数

(1)如何在队列尾部添加元素

offer:可以在队列尾部添加元素(Queue中继承来的)

add:可以在队列尾部添加元素(Queue中继承来的)

offerLast:可以在队列尾部添加元素(Deque额外加的)

addLast:可以在队列尾部添加元素(Deque额外加的)

记住offer就行,add、addLast和offerLast只是实现功能一样,但是没必要用,容易搞乱

(2)如何在队列头部弹出元素

poll:可以获取并删除在队头的元素(Queue中继承来的)

记住poll就行,pollFisrt、remove和removeFirst只是实现功能一样,但是没必要用,容易搞乱

pollFisrt:可以获取并删除在队头的元素(Deque额外加的)

remove:可以获取并删除在队头的元素(Deque额外加的)

removeFirst:可以获取并删除在队头的元素(Deque额外加的)

pollremove的主要区别?

  poll删除队列的首个元素,即使队列为空也不会抛出异常。(类似于offer不抛异常)

  remove队列的首个元素,如果队列为空则会抛出异常。(类似于add抛出异常)

(3)如何获取队列头部的元素

peek:可以获取队列头部的元素(Queue中继承来的)

记住peek就行,peekFirst、getFirst只是实现功能一样,但是没必要用,容易搞乱

peekFirst:可以获取在队头的元素(Deque额外加的)

getFirst:可以获取在队头的元素(Deque额外加的)

peekFirstgetFirst的主要区别?

  peek获取队列的首个元素,即使队列为空也不会抛出异常。(类似于offer不抛异常)

  get获取队列的首个元素,如果队列为空则会抛出异常。(类似于add抛出异常)

四.总结

上面说的有点细了,特别是Deque的方法有点太多,大家可能已经晕了,但是为了理论知识保证,也得给大家写全。下面我就主打一个使用方便,再整理一下子,大家直接记住就行。

1.Stack栈的使用

        pop压栈+push进栈+peek获取栈顶。就这三个特殊的记住就行。

Stack本类Collection继承
压栈

push()

-
出栈pop()-
获取栈顶元素peek()-
判空empty()isEmpty()
长度-size()

2.Queue队列的使用

          poll出队+offer进队+peek获取队头。就这三个特殊的记住就行。

Queue本类Collection继承类
返回null返回异常-
队头出队

poll()

remove()-

peek()

element()
队头获取元素-
队尾入队

offer()

add()-
判空-isEmpty()
长度-size()

 3.Deque双端队列的使用

        Deque是双端队列,只是它能够同时拥有栈和队列的方法。其实Deque实现队列,用的 “poll出队+offer进队+peek获取队头”就是把Queue的继承过来了。而用Deque实现栈,只是增加了pop压栈+push进栈功能,且复用了peek获取队头等价于获取栈顶而已。

        下面的表格看着很复杂方法很多,一方面是因为很多功能相同的操作,分成了返回值null和返回异常两种函数,而返回值异常的这一套函数我们可以不用,主打用返回值为null的。另一方面是,Deque是双端队列,因此增加了很多xxxFirst和xxxLast函数,用于单独对队尾和队头进行操作,而我们实现栈和队列时,完全可以不用这些xxxFirst和xxxLast函数。

Deque本类

Queue继承类

Collection继承类
返回null返回异常返回null返回异常
栈需要的入栈(队头添加)

push()

offerFirst()

addFirst()--
出栈(队头删除)

pop()

pollFirst()

removeFirst()poll()remove()-
获取栈顶(队头获取)peekFirst()getFirst()peek()element()-
队列需要的队头出队

pop()

pollFirst()

removeFirst()poll()remove()-
队头获取元素peekFirst()getFirst()peek()element()-
队尾入队offer()add()offerLast()addLast()
判空----isEmpty()
长度----size()

        终于结束啦!其实大家只要记住Stack和Queue的使用,在Deque里面一样可以用的,Deque只是把Stack和Queue的功能全包括进来了,还自己加了一堆first和last罢了。上面的全部内容会加深你对这三个对象的理解,以及他们直接的继承实现关系。

        但是如果你只想会用,就只要理解和记忆下面6个方法+知道如何定义一个栈or队列or双端队列就行啦。

如果要用Deque实现栈:pop压栈+push进栈+peek获取栈顶。就这三个的记住就行。

如果用Deque实现队列:poll出队+offer进队+peek获取队头。就这三个的记住就行。

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

守岁白驹hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值