当前位置:   article > 正文

java 数据结构栈和队列

java 数据结构栈和队列

目录

栈(Stack)

栈的使用

栈的模拟实现

栈的应用场景

队列(Queue)

队列的使用

队列模拟实现

循环队列

双端队列

用队列实现栈

用栈实现队列


(Stack)

什么是栈?

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


栈的使用

方法例子:

  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. }

栈的模拟实现

从上图中可以看到,Stack继承了VectorVectorArrayList类似,都是动态的顺序表,不同的是
Vector是线程安全的。

模拟实现代码:

  1. public class MyStack {
  2. int[] array;
  3. int size;
  4. public MyStack() {
  5. array = new int[3];
  6. }
  7. public int push(int e) {
  8. ensureCapacity();
  9. array[size++] = e;
  10. return e;
  11. }
  12. public int pop() {
  13. int e = peek();
  14. size--;
  15. return e;
  16. }
  17. public int peek() {
  18. if (empty()) {
  19. throw new RuntimeException("栈为空,无法获取栈顶元素");
  20. }
  21. return array[size - 1];
  22. }
  23. public int size() {
  24. return size;
  25. }
  26. public boolean empty() {
  27. return 0 == size;
  28. }
  29. private void ensureCapacity() {
  30. if (size == array.length) {
  31. array = Arrays.copyOf(array, size * 2);
  32. }
  33. }
  34. }


栈的应用场景

1.逆波兰表达式(中缀转后缀):

https://leetcode.cn/problems/evaluate-reverse-polish-notation/

代码:

  1. class Solution {
  2. public int evalRPN(String[] tokens) {
  3. Stack<Integer> stack=new Stack<>();
  4. for(String x:tokens){
  5. if(!isCase(x)){
  6. stack.push(Integer.parseInt(x));
  7. }else{
  8. int nums2=stack.pop();
  9. int nums1=stack.pop();
  10. switch(x){
  11. case "+":
  12. stack.push(nums1+nums2);
  13. break;
  14. case "-":
  15. stack.push(nums1-nums2);
  16. break;
  17. case "*":
  18. stack.push(nums1*nums2);
  19. break;
  20. case "/":
  21. stack.push(nums1/nums2);
  22. break;
  23. }
  24. }
  25. }
  26. return stack.pop();
  27. }
  28. public boolean isCase(String s){
  29. if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
  30. return true;
  31. }
  32. return false;
  33. }
  34. }

2.括号匹配:

https://leetcode.cn/problems/valid-parentheses/description/

代码:

  1. class Solution {
  2. public boolean isValid(String s) {
  3. Stack<Character> stack = new Stack<>();
  4. //遍历
  5. for (int i = 0; i < s.length(); i++) {
  6. char ch = s.charAt(i);//取出字符
  7. //判断是不是左括号
  8. if (ch == '(' || ch == '{' || ch == '[') {
  9. stack.push(ch);
  10. } else {
  11. //遇到右括号,判断栈是不是空
  12. if (stack.empty()) {
  13. return false;
  14. }
  15. //看看括号匹不匹配
  16. char ch2 = stack.peek();
  17. if ((ch2 == '(' && ch == ')') || (ch2 == '{' && ch == '}') || (ch2 == '[' && ch == ']')) {
  18. stack.pop();
  19. } else {
  20. return false;
  21. }
  22. }
  23. }
  24. //如果栈不空,并且遍历完了
  25. if (!stack.empty()) {
  26. return false;
  27. }
  28. return true;
  29. }
  30. }

3.出栈入栈次序匹配:

https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&&tqId=11174&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

代码:

  1. public boolean IsPopOrder (int[] pushV, int[] popV) {
  2. Stack<Integer> stack = new Stack<>();
  3. int j = 0;
  4. for (int i = 0; i < pushV.length; i++) {
  5. stack.push(pushV[i]);
  6. //每进栈一个数字,peek一下判断一不一样,并且判断越没越界
  7. while (!stack.empty() && j < popV.length && stack.peek() == popV[j]) {
  8. stack.pop();
  9. j++;
  10. }
  11. }
  12. //循环走完了,判断是不是空,是的话就匹配了,不是的话就不匹配
  13. return stack.empty();
  14. }

4.最小栈:

https://leetcode.cn/problems/min-stack/

代码:

  1. class MinStack {
  2. public Stack<Integer> stack;
  3. public Stack<Integer> minStack;
  4. public MinStack() {
  5. stack = new Stack<>();
  6. minStack = new Stack<>();
  7. }
  8. public void push(int val) {
  9. stack.push(val);
  10. if (minStack.empty()) {
  11. minStack.push(val);
  12. } else {
  13. int minVal = minStack.peek();
  14. //判断最小栈的栈顶是不是小于等于要存入的值
  15. if (val <= minVal) {
  16. minStack.push(val);
  17. }
  18. }
  19. }
  20. public void pop() {
  21. if (stack.peek().equals(minStack.peek())) {
  22. // 弹出的元素是全栈最小的
  23. minStack.pop();
  24. }
  25. stack.pop();
  26. }
  27. public int top() {
  28. return stack.peek();
  29. }
  30. public int getMin() {
  31. if (!minStack.empty()) {
  32. return minStack.peek();
  33. }
  34. return -1;
  35. }
  36. }


队列(Queue)

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


队列的使用

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

注意: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. }


队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常
见的空间类型有 两种:顺序结构 和 链式结构 。同学们思考下: 队列的实现使用顺序结构还是链式
结构好?

模拟实现代码:

  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. }


循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会

使用循环队列。 环形队列通常使用数组实现。

https://leetcode.cn/problems/design-circular-queue/description/

代码:

  1. public class MyCircularQueue {
  2. public int[] elem;
  3. public int front; // 队头
  4. public int rear; // 队尾
  5. public int usedSize; // 记录队列中元素数量
  6. public MyCircularQueue(int k) {
  7. elem = new int[k];
  8. usedSize = 0; // 初始化为 0
  9. }
  10. // 入队
  11. public boolean enQueue(int value) {
  12. if (isFull()) {
  13. return false;
  14. }
  15. elem[rear] = value;
  16. rear = (rear + 1) % elem.length;
  17. usedSize++; // 每次入队增加 usedSize
  18. return true;
  19. }
  20. // 出队
  21. public boolean deQueue() {
  22. if (isEmpty()) {
  23. return false;
  24. }
  25. front = (front + 1) % elem.length;
  26. usedSize--; // 每次出队减少 usedSize
  27. return true;
  28. }
  29. // 得到队头元素
  30. public int Front() {
  31. if (isEmpty()) {
  32. return -1;
  33. }
  34. return elem[front];
  35. }
  36. // 得到队尾元素
  37. public int Rear() {
  38. if (isEmpty()) {
  39. return -1;
  40. }
  41. //如果是0下标就返回数组长度-1不是就rear-1
  42. int index = (rear == 0) ? elem.length - 1 : rear - 1;
  43. return elem[index];
  44. }
  45. public boolean isEmpty() {
  46. return usedSize == 0; // 使用 usedSize 判断是否为空
  47. }
  48. public boolean isFull() {
  49. return usedSize == elem.length; // 使用 usedSize 判断是否为满
  50. }
  51. }


双端队列

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque “double ended

queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

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

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

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

用队列实现栈

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

代码:

  1. class MyStack {// 用队列实现栈
  2. //用两个队列实现
  3. public Deque<Integer> qu1;
  4. public Deque<Integer> qu2;
  5. public MyStack() {
  6. qu1 = new LinkedList<>();
  7. qu2 = new LinkedList<>();
  8. }
  9. public void push(int x) {
  10. if (!qu1.isEmpty()) {
  11. qu1.offer(x);
  12. } else if (!qu2.isEmpty()) {
  13. qu2.offer(x);
  14. } else {
  15. //都空的,指定存
  16. qu1.offer(x);
  17. }
  18. }
  19. public int pop() {
  20. if (empty()) {
  21. return -1;
  22. }
  23. if (!qu1.isEmpty()) {
  24. //这样记录不会因为poll改变
  25. int size = qu1.size();
  26. for (int i = 0; i < size - 1; i++) {//取到长度减1个就是要的了
  27. int x = qu1.poll();
  28. qu2.offer(x);
  29. }
  30. return qu1.poll();
  31. } else {
  32. int size = qu2.size();
  33. for (int i = 0; i < size - 1; i++) {//取到长度减1个就是要的了
  34. int x = qu2.poll();
  35. qu1.offer(x);
  36. }
  37. return qu2.poll();
  38. }
  39. }
  40. public int top() {
  41. if (empty()) {
  42. return -1;
  43. }
  44. if (!qu1.isEmpty()) {
  45. //这样记录不会因为poll改变
  46. int size = qu1.size();
  47. int x = 0;//用来记录拿出来的值,循环结束后就拿到最后一个
  48. for (int i = 0; i < size; i++) {
  49. x = qu1.poll();
  50. qu2.offer(x);
  51. }
  52. return x;
  53. } else {
  54. int size = qu2.size();
  55. int x = 0;//用来记录拿出来的值,循环结束后就拿到最后一个
  56. for (int i = 0; i < size ; i++) {
  57. x = qu2.poll();
  58. qu1.offer(x);
  59. }
  60. return x;
  61. }
  62. }
  63. public boolean empty() {
  64. //判断两个队列是不是都空的
  65. return qu1.isEmpty() && qu2.isEmpty();
  66. }
  67. }

用栈实现队列

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

代码:

  1. public class MyQueue {
  2. public Stack<Integer> s1;
  3. public Stack<Integer> s2;
  4. public MyQueue() {
  5. s1 = new Stack<>();
  6. s2 = new Stack<>();
  7. }
  8. public void push(int x) {
  9. s1.push(x);
  10. }
  11. public int pop() {
  12. if (empty()) {
  13. return -1;
  14. }
  15. if (s2.isEmpty()) {
  16. while (!s1.isEmpty()) {
  17. s2.push(s1.pop());
  18. }
  19. }
  20. return s2.pop();
  21. }
  22. public int peek() {
  23. if (empty()) {
  24. return -1;
  25. }
  26. if (s2.isEmpty()) {
  27. int size = s1.size();
  28. for (int i = 0; i < size; i++) {
  29. int x = s1.pop();
  30. s2.push(x);
  31. }
  32. }
  33. return s2.peek();
  34. }
  35. public boolean empty() {
  36. return s1.isEmpty() && s2.isEmpty();
  37. }
  38. }

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

闽ICP备14008679号