数据结构_队列

队列

1.队列是什么

一种特殊的线性表,特点是先进先出:

  • 先进,表示队列的数据新增操作只能在队尾进行
  • 先出,表示队列的数据删除操作只能在队头进行
    在这里插入图片描述

存储方式可以分为:链式存储和顺序存储

顺序存储会存在“假溢出”的现象,即数组中元素并未存满(队首移除了元素,但是队尾指针已指向最后一个数组下标)
两个粗暴的解决方案为:

  • 消耗O(n)的时间复杂度去移动数据(向队头方向)
  • 开辟足够大的空间保证数组不会越界

在这里插入图片描述

还有通过队列的一个特殊变种来解决,叫做循环队列。

循环队列进行新增数据元素操作时,首先判断队列是否为满。如果不满,则可以将新元素赋值给队尾,然后让 rear 指针向后移动一个位置。如果已经排到队列最后的位置,则 rear指针重新指向头部。

循环队列进行删除操作时,即出队列操作,需要判断队列是否为空,然后将队头元素赋值给返回值,front 指针向后移一个位置。如果已经排到队列最后的位置,就把 front 指针重新指向到头部。

样就能在不开辟大量存储空间的前提下,不采用 O(n) 的操作,也能通过移动数据来完成频繁的新增和删除数据。

但是这样又存在一个问题,当 front 指针和 rear 指针相等时,无法判断队满还是队空。解决方案有三:

  • 牺牲一个的单元,约定当front指向rear后的一个单元则代表队满,如图3-7(d2)
    队满条件为:(Q.rear+1)%MaxSize == Q.front
    队空条件: Q.rear==Q.front
    队列长度: (Q.rear - Q.front + MaxSize ) % MaxSize

  • 新增一个数据成员(size)来表示元素个数
    则 Q.size = 0为空 Q.size = MaxSize 为满

  • 新增一个数据成员(flag)来表示情况,flag=1下删除导致的rear == front则为队空,flag=2下插入导致的rear==front则为队满
    在这里插入图片描述

链式存储就是一个单链表,同时增加了 front 指针和 rear 指针。链式队列和单链表一样,通常会增加一个头结点,并另 front 指针指向头结点。头结点不存储数据,只是用来辅助标识。这主要是为了防止删除最后一个有效数据结点后, front 指针和 rear 指针变成野指针,导致队列没有意义了。有了头结点后,哪怕队列为空,头结点依然存在,能让 front 指针和 rear 指针依然有意义。

在这里插入图片描述

双端队列

在这里插入图片描述

2.队列的案例

约瑟夫环问题。约瑟夫环是一个数学的应用问题,具体为,已知 n 个人(以编号 1,2,3…n 分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出列;他的下一个人又从 1 开始报数,数到 m 的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

解析:
这个问题的输入变量就是 n 和 m,即 n 个人和数到 m 的出列的人。输出的结果,就是 n 个人出列的顺序。

这个问题,用队列的方法实现是个不错的选择。它的结果就是出列的顺序,恰好满足队列对处理顺序敏感的前提。因此,求解方式也是基于队列的先进先出原则。解法如下:

1.先把所有人都放入循环队列中。注意这个循环队列的长度要大于或者等于 n。

2.从第一个人开始依次出队列,出队列一次则计数变量 i 自增。如果 i 比 m 小,则还需要再入队列。

3.直到i等于 m 的人出队列时,就不用再让这个人进队列了。而是放入一个用来记录出队列顺序的数组中。

4.直到数完 n 个人为止。当队列为空时,则表示队列中的 n 个人都出队列了,这时结束队列循环,输出数组内记录的元素。

代码实现:

package cn.iyhome;

import java.util.LinkedList;

public class Josephus {
    public static void main(String[] args) {
            Jose(10,5);
    }

    public static void Jose(int n, int m) {

        // 创建链式队列
        LinkedList<Integer> queue = new LinkedList<Integer>();
        for (int i = 1; i < n + 1; i++) {
            queue.add(i);
        }

        // 从编号2开始报数
        int k = 2;
        int element = 0;
        for (int i = 1; i < k; i++) {
            element = queue.poll();//从队首出队
            queue.add(element);//将该元素从队尾入队
        }// 至此,队列已满足题目中从编号2开始报数的要求

        //
        int count = 1;
        while (queue.size() > 0) {
            element = queue.poll();//出队
            if (count < m) {//没有数到m
                queue.add(element);//入队到队尾
                count++;//继续报数
            } else {//数到m,不入队
                count = 1;//重新从1开始报数
                System.out.println(element);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值