• 数据结构之队列


     💕"

    开朗些,勇敢些

    "💕

    作者:Mylvzi 

     文章主要内容:数据结构之队列及其面试题 

    一.队列

    1.概念

      队列:只允许在一端进行数据的插入(队尾),在另一端进行数据的删除(队头)的特殊线性表,和栈不同的是,队列具有先进先出的特性(FIFO)

    2.java中的队列

      Java中队列是一个接口,底层是通过链表实现的;既然是接口,就不能直接实例化对象,要利用实现queue接口的类去实例化对象

    源码

    二.队列的模拟实现

      队列的底层是是LinkdList,所以可以使用双向链表实现队列

    1. /**
    2. * 实现队尾进,队头出
    3. */
    4. public void offer(int x) {
    5. ListNode node = new ListNode(x);
    6. if (rear == null) {
    7. front = rear = node;
    8. this.usedSize++;
    9. return;
    10. }
    11. rear.next = node;
    12. node.prev = rear;
    13. rear = node;
    14. this.usedSize++;
    15. }
    16. public int poll() {
    17. if (front == null) {
    18. throw new QueueEmptyException("队列为空,无法返回数据");
    19. }
    20. int ret = front.val;
    21. // 如果只有一个节点 要把front和rear都置空 否则在执行front.prev = null这句代码时会报错
    22. if (front.next == null) {
    23. front = null;
    24. rear = null;
    25. return ret;
    26. }
    27. front = front.next;
    28. front.prev = null;
    29. this.usedSize--;
    30. return ret;
    31. }
    32. public int peek() {
    33. if (front == null) {
    34. throw new QueueEmptyException("队列为空,无法返回数据");
    35. }
    36. int ret = front.val;
    37. return ret;
    38. }
    39. public int size() {
    40. return this.usedSize;
    41. }
    42. public boolean empty() {
    43. return this.usedSize==0;
    44. }
    45. }

     3.Deque

      Deque也是Java中和队列有关的数据结构,它是一种双端队列,即队头,队尾都可以实现数据插入与删除,deque 是 “double ended queue” 的简称。同时Deque也是Java中最常用的queue结构  

    源码:

    4.循环队列的实现

    所谓循环队列就是front和rear一直在绕着圆走,和普通队列不一样的是,它可以重复利用空间

    循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

    循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

    一般的队列

    循环队列

    循环队列的难点在于如何判满,这里我们提供两种方法 

    循环队列的实现1:使用计数器

    1. class MyCircularQueue {
    2. private int[] elem;
    3. private int front;
    4. private int rear;
    5. private int cnt = 0;
    6. public MyCircularQueue(int k) {
    7. this.elem = new int[k];
    8. }
    9. public boolean enQueue(int value) {
    10. // 插入数据
    11. if(isFull()) {
    12. return false;
    13. }
    14. this.elem[rear] = value;
    15. rear = (rear+1) % elem.length;
    16. cnt++;
    17. return true;
    18. }
    19. public boolean deQueue() {
    20. if(isEmpty()) {
    21. return false;
    22. }
    23. front = (front+1) % elem.length;
    24. cnt--;
    25. return true;
    26. }
    27. public int Front() {
    28. if(isEmpty()) {
    29. return -1;
    30. }
    31. return this.elem[front];
    32. }
    33. public int Rear() {
    34. if (isEmpty()) {
    35. return -1;
    36. }
    37. int index = (rear == 0) ? (elem.length-1) : (rear-1);
    38. return this.elem[index];
    39. }
    40. public boolean isEmpty() {
    41. return cnt == 0;
    42. }
    43. public boolean isFull() {
    44. return cnt == this.elem.length;
    45. }
    46. }

     循环队列的实现2:预留一个空间

    1. class MyCircularQueue {
    2. private int[] elem;
    3. private int front;
    4. private int rear;
    5. public MyCircularQueue(int k) {
    6. this.elem = new int[k+1];
    7. }
    8. public boolean enQueue(int value) {
    9. // 插入数据
    10. if(isFull()) {
    11. return false;
    12. }
    13. this.elem[rear] = value;
    14. rear = (rear+1) % elem.length;
    15. return true;
    16. }
    17. public boolean deQueue() {
    18. if(isEmpty()) {
    19. return false;
    20. }
    21. front = (front+1) % elem.length;
    22. return true;
    23. }
    24. public int Front() {
    25. if(isEmpty()) {
    26. return -1;
    27. }
    28. return this.elem[front];
    29. }
    30. public int Rear() {
    31. if(isEmpty()) {
    32. return -1;
    33. }
    34. int index = (rear == 0) ? (elem.length-1) : (rear-1);
    35. return this.elem[index];
    36. }
    37. public boolean isEmpty() {
    38. return rear == front;
    39. }
    40. public boolean isFull() {
    41. return (rear+1) % elem.length == front;
    42. }
    43. }

    注意:为了实现循环,rear和front的变化不能直接写成rear++或者front++,否则会发生越界

    rear = (rear+1) % elem.length;

    front = (front+1) % elem.length;

    用队列实现栈

    225. 用队列实现栈 - 力扣(LeetCode)

    1.使用两个队列

      利用两个队列实现栈

    如果是入栈,入到不为空的队列之中

    如果是出栈,先找到不为空的队列,将队尾之前的所有元素出到另一个队列之中,最后再出队尾元素,实现后进先出

    如果是返回栈顶元素,和出栈的思路一样,栈顶就是不为空队列的队尾

    栈为空:当两个队列都为空时,栈为空

    1. /**
    2. * 用队列实现栈 使用两个队列实现栈
    3. * 队列:先进先出 栈:后进先出
    4. * 要明白,一个队列无法实现栈,他们有本质上的区别
    5. * 入栈:入到不为空的队列之中 如果两个都为空,入到第一个队列之中
    6. * 出栈:出栈顶元素,后进先出 对应的是队列中的队尾元素 将队尾元素之前的所有元素转移到另一个队列之中,再返回队尾元素
    7. */
    8. class MyStack2 {
    9. private Queue que1;
    10. private Queue que2;
    11. public MyStack2() {
    12. this.que1 = new LinkedList<>();
    13. this.que2 = new LinkedList<>();
    14. }
    15. public void push(int x) {
    16. // 入栈到不为空的队列之中
    17. if(!que1.isEmpty()) {
    18. que1.offer(x);
    19. } else if (!que2.isEmpty()) {
    20. que2.offer(x);
    21. }else {
    22. que1.offer(x);
    23. }
    24. }
    25. public int pop() {
    26. // 出栈
    27. // 为空 直接返回
    28. if (empty()) {
    29. return -1;
    30. }
    31. if (!que1.isEmpty()) {
    32. int size = que1.size();
    33. for (int i = 0; i < size - 1; i++) {
    34. // 将que1队尾元素前面的所有元素转移到que2中
    35. que2.offer(que1.poll());
    36. }
    37. return que1.poll();
    38. } else {
    39. int size = que2.size();
    40. for (int i = 0; i < size - 1; i++) {
    41. // 将que2队尾元素前面的所有元素转移到que1中
    42. que1.offer(que2.poll());
    43. }
    44. return que2.poll();
    45. }
    46. }
    47. public int top() {
    48. // 为空 直接返回
    49. if (empty()) {
    50. return -1;
    51. }
    52. int tmp = -1;
    53. if (!que1.isEmpty()) {
    54. int size = que1.size();
    55. for (int i = 0; i < size; i++) {
    56. // 将que1队尾元素前面的所有元素转移到que2中
    57. tmp = que1.poll();
    58. que2.offer(tmp);
    59. }
    60. return tmp;
    61. } else {
    62. int size = que2.size();
    63. for (int i = 0; i < size - 1; i++) {
    64. // 将que2队尾元素前面的所有元素转移到que1中
    65. tmp = que2.poll();
    66. que1.offer(tmp);
    67. }
    68. return tmp;
    69. }
    70. }
    71. public boolean empty() {
    72. // 当两个队列都为空时,栈就为空
    73. return que1.isEmpty() && que2.isEmpty();
    74. }
    75. }

     2.使用一个队列

    1. /**
    2. * 思路2:使用一个队列实现栈
    3. * 栈:后进先出 队列:先进后出
    4. * 保证队头的元素时最后一个入栈的
    5. * 新入一个元素的时候将之前所有元素都poll出去,再重新入队,那么新元素就是队头了
    6. * 这样就实现了后进先出
    7. */
    8. class MyStack2 {
    9. Queue queue;
    10. public MyStack2() {
    11. this.queue = new LinkedList<>();
    12. }
    13. public void push(int x) {
    14. int n = queue.size();
    15. queue.offer(x);
    16. for (int i = 0; i < n; i++) {
    17. // 重新入队
    18. queue.offer(queue.poll());
    19. // int ret = queue.poll();
    20. // queue.offer(ret);
    21. }
    22. }
    23. public int pop() {
    24. return queue.poll();
    25. }
    26. public int top() {
    27. return queue.peek();
    28. }
    29. public boolean empty() {
    30. return queue.isEmpty();
    31. }
    32. }

    可以带入数据尝试一下  之所以需要%N,是因为rear有可能处于下标为0的位置

  • 相关阅读:
    LC-6248. 统计中位数为 K 的子数组(回文:中心扩散+哈希、等价转换)【周赛321】
    npm install报错,解决记录
    web课程设计:HTML非遗文化网页设计题材【京剧文化】HTML+CSS+JavaScript
    MyBatisPlus(二十)防全表更新与删除
    飘了,英特尔2年内要发布高效芯片超过苹果M1
    C语言之文件操作篇(1)
    Mysql系列一:事物概念及特性
    迅为IMX6开发板QT系统创建AP热点基于RTL8723交叉编译hostapd
    shallow fusion--学习笔记
    再见 Excel,你好 Python Spreadsheets!⛵
  • 原文地址:https://blog.csdn.net/Mylvzi/article/details/134531373