当前位置:   article > 正文

手把手教你Java实现栈和队列_java 队列 栈实现类

java 队列 栈实现类

目录

一、栈(Stack)

1、概念

2、栈的使用 

3、栈的模拟实现

4、栈的应用场景

2. 队列(Queue)

1、概念

2、队列的使用  

3、队列模拟实现

4、循环队列

三、双端队列 (Deque) 

五、栈和队列的互相实现

用队列实现栈:

用栈实现队列:


一、(Stack)

1、概念

:一种特殊的线性表,其 只允许在固定的一端进行插入和删除元素操作 。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO Last In First Out )的原则。
压栈 :栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶
出栈 :栈的删除操作叫做出栈。 出数据在栈顶

栈的push 和 pop操作大概可以用下面这张图来进行表示:

栈在现实生活中的例子:


2、栈的使用 

在Java中,关于栈的常用的方法有以下几个:

我们可以用代码来感受一下这些方法的使用:

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

3、栈的模拟实现

从上图中可以看到, Stack 继承了 Vector Vector ArrayList 类似,都是动态的顺序表,不同的是 Vector 是线程安全的。
那么现在,我们可以运用目前所学过的知识来模拟一下栈中的各个方法:
push方法
  1. public int push(int e){
  2. ensureCapacity();
  3. array[size++] = e;
  4. return e;
  5. }

pop方法

  1. public int pop(){
  2. int e = peek();
  3. size--;
  4. return e;
  5. }

peek方法

  1. public int peek(){
  2. if(empty()){
  3. throw new RuntimeException("栈为空,无法获取栈顶元素");
  4. }
  5. return array[size-1];
  6. }

4、栈的应用场景

1. 改变元素的序列
现在,我们来看这样一道题:

 我们可以根据栈的先进后出的特性来对其进行分析

我们可以发现,A选项中是先将1入栈再出栈,再将234依次入栈后出栈所得到的结果

                         B选项是先将1入栈,再将234轮流入栈出栈,最后将1出栈得到的结果

                         C选项是不可能做到的,因为如果按照31的顺序出栈,那么2必然先比1出栈

                         D选项则是先把12入栈,再将34轮流进行出栈入栈的操作后,再将12出栈

根据这道例题,我们发现可以运用栈来改变元素的序列

2. 将递归转化为循环   

假设现在有这么一道oj题:要求我们逆序打印链表
那么递归的写法是这个样子的:
  1. // 递归方式
  2. void printList(Node head){
  3. if(null != head){
  4. printList(head.next);
  5. System.out.print(head.val + " ");
  6. }
  7. }

而如果我们使用栈,那么代码就会演变成这样:

  1. void printList(Node head){
  2. if(null == head){
  3. return;
  4. }
  5. Stack<Node> s = new Stack<>();
  6. // 将链表中的结点保存在栈中
  7. Node cur = head;
  8. while(null != cur){
  9. s.push(cur);
  10. cur = cur.next;
  11. }
  12. // 将栈中的元素出栈
  13. while(!s.empty()){
  14. System.out.print(s.pop().val + " ");
  15. }
  16. }

综上,我们可以得知:通过栈的实现,我们可以将递归转化成循环


2. 队列(Queue)

1、概念

队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾(Tail/Rear) 出队列:进行删除操作的一端称为 队头(Head/Front)

2、队列的使用  

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

那么在Java中,常见的队列的方法有以下几种:

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

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

3、队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种: 顺序结构 和 链式结构

  1. public class Queue {
  2. // 双向链表节点
  3. public static class ListNode{
  4. ListNode next;
  5. ListNode prev;
  6. int value;
  7. ListNode(int value){
  8. this.value = value;
  9. }
  10. }
  11. ListNode first; // 队头
  12. ListNode last; // 队尾
  13. int size = 0;
  14. // 入队列---向双向链表位置插入新节点
  15. public void offer(int e){
  16. ListNode newNode = new ListNode(e);
  17. if(first == null){
  18. first = newNode;
  19. // last = newNode;
  20. }else{
  21. last.next = newNode;
  22. newNode.prev = last;
  23. // last = newNode;
  24. }
  25. last = newNode;
  26. size++;
  27. }
  28. // 出队列---将双向链表第一个节点删除掉
  29. public int poll(){
  30. // 1. 队列为空
  31. // 2. 队列中只有一个元素----链表中只有一个节点---直接删除
  32. // 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
  33. int value = 0;
  34. if(first == null){
  35. return null;
  36. }else if(first == last){
  37. last = null;
  38. first = null;
  39. }else{
  40. value = first.value;
  41. first = first.next;
  42. first.prev.next = null;
  43. first.prev = null;
  44. }
  45. --size;
  46. return value;
  47. }
  48. // 获取队头元素---获取链
  49. public int peek(){
  50. if(first == null){
  51. return null;
  52. }
  53. return first.value;
  54. }
  55. public int size() {
  56. return size;
  57. }
  58. public boolean isEmpty(){
  59. return first == null;
  60. }
  61. }

4、循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。
环形队列的结构如下图所示:

数组下标循环的小技巧 

1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset)%array.length

 如何区分空与满

1. 通过添加 size 属性记录
2. 保留一个位置
3. 使用标记


三、双端队列 (Deque) 

双端队列( deque )是指允许两端都可以进行入队和出队操作的队列, deque “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

 Deque是一个接口,使用时必须创建LinkedList的对象。

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

  1. Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
  2. Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

五、栈和队列的互相实现

用队列实现栈:

  1. class MyStack {
  2. Queue<Integer> queue1;
  3. Queue<Integer> queue2;
  4. public MyStack() {
  5. queue1 = new LinkedList<Integer>();
  6. queue2 = new LinkedList<Integer>();
  7. }
  8. public void push(int x) {
  9. queue2.offer(x);
  10. while (!queue1.isEmpty()) {
  11. queue2.offer(queue1.poll());
  12. }
  13. Queue<Integer> temp = queue1;
  14. queue1 = queue2;
  15. queue2 = temp;
  16. }
  17. public int pop() {
  18. return queue1.poll();
  19. }
  20. public int top() {
  21. return queue1.peek();
  22. }
  23. public boolean empty() {
  24. return queue1.isEmpty();
  25. }
  26. }

用栈实现队列:

  1. class MyQueue {
  2. public Stack<Integer> stack1;
  3. public Stack<Integer> stack2;
  4. public MyQueue() {
  5. stack1 = new Stack<>();
  6. stack2 = new Stack<>();
  7. }
  8. public void push(int x) {
  9. stack1.push(x);
  10. }
  11. public int pop() {
  12. if (stack2.isEmpty()) {
  13. in2out();
  14. }
  15. return stack2.pop();
  16. }
  17. public int peek() {
  18. if (stack2.isEmpty()){
  19. in2out();
  20. }
  21. return stack2.peek();
  22. }
  23. public boolean empty() {
  24. return stack1.isEmpty() && stack2.isEmpty();
  25. }
  26. private void in2out() {
  27. while (!stack1.isEmpty()) {
  28. stack2.push(stack1.pop());
  29. }
  30. }
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/611883
推荐阅读
相关标签
  

闽ICP备14008679号