• Stack和Queue 栈和队列


    目录

    一、栈(Stack)

    1.概念

    2.栈的使用

    3.栈的模拟实现

    初始化

    压栈(入栈)

    弹出栈顶元素(出栈)

    peek操作

    获取栈元素个数

    栈的练习--反转链表

    二、队列(Queue)

    概念

    队列的使用

    队列的模拟实现

    初始化

    入队

    出队

    peek() 

    判断队列是否为空

    循环队列

    循环结构

    代码实现

    初始化

    isFull() 和 isEmpty()

    入队

    出队

    peek()

    获取队尾元素

    三、双端队列

    概念


    一、栈(Stack)

    1.概念

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

    栈顶(Top):线性表允许进行插入删除的那一端。
    栈底(Bottom):固定的,不允许进行插入和删除的另一端。
    空栈:不含任何元素的空表。

    2.栈的使用

    方法功能
    Stack()构造一个空栈
    E push(E e)讲e入栈,并返回e
    E pop()讲栈顶元素出栈并返回
    E peek()获取栈顶元素
    int size()获取栈中元素个数
    boolean isEmpty()判断栈是否为空

    3.栈的模拟实现

    底层用数组

    初始化

    1. public int[] elem;
    2. //当前栈 当中 存储的有效的数据个数 也可以当中 当前可以存放数据元素的下标
    3. public int usedSize;
    4. public static final int DEFAULT_CAPACITY = 10;
    5. public MyStack() {
    6. elem = new int[DEFAULT_CAPACITY];
    7. }

    压栈(入栈)

    在进行此步操作是需要判断数组是否已满,来确定是否需要扩容操作。

    1. /**
    2. * 压栈
    3. */
    4. public void push(int val) {
    5. //1、判断栈是否是满的
    6. if(isFull()) {
    7. elem = Arrays.copyOf(elem,2*elem.length);
    8. }
    9. //存放到当前的下标,同时usedSize需要自增
    10. elem[usedSize] = val;
    11. usedSize++;
    12. }
    13. /**
    14. * 判断 当前栈是否是满的
    15. * @return
    16. */
    17. public boolean isFull() {
    18. if(usedSize == elem.length) {
    19. return true;
    20. }
    21. return false;
    22. }

    弹出栈顶元素(出栈)

    此步需要判断此时栈是否为空,以为栈为空的话无法弹出元素。

    1. /**
    2. * 删除栈顶元素
    3. * @return
    4. */
    5. public int pop(){
    6. if(isEmpty()) {
    7. throw new EmptyStackException("栈为空了!");
    8. }
    9. int oldVal = elem[usedSize-1];
    10. usedSize--;
    11. return oldVal;
    12. }
    13. /**
    14. * 是否为空
    15. * @return
    16. */
    17. public boolean isEmpty() {
    18. return usedSize == 0;
    19. }

    peek操作

    这里也需要判空。

    1. /**
    2. * 获取栈顶元素 但是不删除
    3. * @return
    4. */
    5. public int peek() {
    6. if(isEmpty()) {
    7. throw new EmptyStackException("栈为空了!");
    8. }
    9. return elem[usedSize-1];
    10. }

    获取栈元素个数

    因为我每一次入栈usedSize都会++,所以这里直接返回usedSize就行了。

    1. public int getUsedSize() {
    2. return usedSize;
    3. }

    栈的练习--反转链表

     思路:因为栈是先进后出的。实现原理就是把链表节点一个个入栈,当全部入栈完之后再一个个出栈,出栈的时候在把出栈的结点串成一个新的链表。

    1. public ListNode ReverseList(ListNode head) {
    2. Stack stack= new Stack<>();
    3. //把链表节点全部摘掉放到栈中
    4. while (head != null) {
    5. stack.push(head);
    6. head = head.next;
    7. }
    8. if (stack.isEmpty()) {
    9. return null;
    10. }
    11. ListNode node = stack.pop();
    12. ListNode dummy = node;
    13. //栈中的结点全部出栈,然后重新连成一个新的链表
    14. while (!stack.isEmpty()) {
    15. ListNode tempNode = stack.pop();
    16. node.next = tempNode;
    17. node = node.next;
    18. }
    19. //要让最后一个节点的next等于空,否则会构成环
    20. node.next = null;
    21. return dummy;
    22. }

    二、队列(Queue)

    概念

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

    队列的使用

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

    方法功能
    boolean offer(E e)入队
    E poll()出队
    peek()获取队头元素
    int size()获取队列中元素的个数
    boolean isEmpty()判断队列是否为空

    注意:

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

    队列的模拟实现

    刚才说了队列是一个接口,底层是通过链表来实现的,这里我也是通过链表实现的。

    初始化

    1. static class Node {
    2. public int val;
    3. public Node next;
    4. public Node(int val) {
    5. this.val = val;
    6. }
    7. }
    8. public Node head;//队列的头
    9. public Node tail;//队列的尾

    入队

    1. /**
    2. * 入队操作
    3. * @param val
    4. */
    5. public void offer(int val) {
    6. Node node = new Node(val);
    7. if(head == null) {
    8. head = node;
    9. tail = node;
    10. }else {
    11. tail.next = node;
    12. tail = tail.next;
    13. }
    14. }

    出队

    1. /**
    2. * 出队
    3. */
    4. public int poll() {
    5. if(head == null) {
    6. return -1;//或者抛异常
    7. }
    8. int oldVal = head.val;
    9. if(head.next == null) {
    10. head = tail = null;
    11. }else {
    12. head = head.next;
    13. }
    14. return oldVal;
    15. }

    peek() 

    1. //查看当前队头元素是几
    2. public int peek() {
    3. if(head == null) {
    4. return -1;//或者抛异常
    5. }
    6. return head.val;
    7. }

    判断队列是否为空

    1. // 队列是否为空
    2. public boolean isEmpty() {
    3. return head == null;
    4. }

    循环队列

    在我们用数组实现队列时,很容易想到下面这个结构:

     但是当我们连续从该队头中弹出元素时,就可以发现问题了:

     可以看到此时数组并没有满,但是当我们再次插入元素时,队尾却插入不了了,这时候我们可以想到将该数组看成是循环的数组,结构如下:

    循环结构

    代码实现

    首先:

    1、设置usedSize 当usedSize和数组长度相等时为满,等于0 则为空。
    2、设置标志位 设 flag = true,每放一个元素,将 flag 置为 false,每有一个元素出队列,则将 flag 置为 true。当 front 和 rear 相遇时,flag为 true 则是空的,反之则是满的。

    初始化

    1. public int[] elem;
    2. public int front;// 队头下标
    3. public int rear;// 队尾下标
    4. boolean flag = true;// 是否为空
    5. public MyCircularQueue(int k) {
    6. elem = new int[k];
    7. }

    isFull() 和 isEmpty()

    1. // 检查循环队列是否为空。
    2. public boolean isEmpty() {
    3. if (rear == front && flag){
    4. return true;
    5. }
    6. return false;
    7. }
    8. // 检查循环队列是否已满。
    9. public boolean isFull() {
    10. if (rear == front && !flag){
    11. return true;
    12. }
    13. return false;
    14. }

    入队

    1. //向循环队列插入一个元素。如果成功插入则返回真。
    2. public boolean enQueue(int value) {
    3. if (isFull()) {
    4. return false;
    5. }
    6. elem[rear] = value;
    7. rear = (rear + 1) % elem.length;
    8. flag = false;
    9. return true;
    10. }

    出队

    1. //从循环队列中删除一个元素
    2. public boolean deQueue() {
    3. if (isEmpty()) {
    4. return false;
    5. }
    6. front = (front + 1) % elem.length;
    7. flag = true;
    8. return true;
    9. }

    peek()

    1. //从队首获取元素。如果队列为空,返回 -1 。
    2. public int Front() {
    3. if (isEmpty()) {
    4. return -1;
    5. }
    6. return elem[front];
    7. }

    获取队尾元素

    1. //获取队尾元素。如果队列为空,返回 -1 。
    2. public int Rear() {
    3. if (isEmpty()) {
    4. return -1;
    5. }
    6. // 如果是0下标,拿最后一个元素
    7. if (rear == 0) {
    8. return elem[elem.length-1];
    9. }else {
    10. return elem[rear - 1];
    11. }
    12. }

    三、双端队列

    概念

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

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

  • 相关阅读:
    golang中的select原理解析
    Archimedean property
    《菜狗商城》Springboot+Vue电商项目
    @Validated和@Valid 区别
    SpringBoot的Condition注解
    js页面window.onload()=$(function(){}) 和$(docunment).ready(function(){})
    其他网络应用
    C语言 coding style
    Ceph入门到精通-Macvlan网络模式
    【办公类-22-14】周计划系列(5-5)“周计划-05 周计划表格内教案部分“节日”清空改成“节日“” (2024年调整版本)Win32
  • 原文地址:https://blog.csdn.net/qq_52592775/article/details/126081617