C#队列

  队列(Queue)是插入操作限定在表的尾部而其它操作限定在表的头部进行的线性表。把进行插入操作的表尾称为队尾(Rear),把进行其它操作的头部称为队头(Front)。当对列中没有数据元素时称为空对列(Empty Queue)。对列的操作是按照先进先出(First In First Out)或后进后出( Last In Last Out)的原则进行的,因此,队列又称为FIFO表或LILO表。队列Q的操作示意图如图所示。

  队列的形式化定义为:队列(Queue)简记为 Q,是一个二元组,

  Q = (D, R)

    其中:D 是数据元素的有限集合;

    R 是数据元素之间关系的有限集合。

  队列的操作是线性表操作的一个子集。队列的操作主要包括在队尾插入元素、在队头删除元素、取队头元素和判断队列是否为空等。与栈一样,队列的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在物理存储结构层次上的。因此,把队列的操作作为逻辑结构的一部分,每个操作的具体实现只有在确定了队列的存储结构之后才能完成。

  队列接口 IQueue<T>的定义如下所示。

public interface IQueue<T>//队列接口的定义
    {
        int GetLength(); //求队列的长度
        bool IsEmpty(); //判断对列是否为空
        void Clear(); //清空队列
        void In(T item); //入队
        T Out(); //出队
        T GetFront(); //取对头元素
    }

队列的存储和运算实现

  1. 顺序队列

用一片连续的存储空间来存储队列中的数据元素, 这样的队列称为顺序队列 (Sequence Queue)。类似于顺序栈,用一维数组来存放顺序队列中的数据元素。队头位置设在数组下标为 0 的端,用 front 表示;队尾位置设在数组的另一端,用 rear 表示。 front 和 rear 随着插入和删除而变化。 当队列为空时, front=rear=-1。图是顺序队列的两个指示器与队列中数据元素的关系图。

  当有数据元素入队时,队尾指示器 rear 加 1,当有数据元素出队时,队头指示器 front 加 1。当 front=rear 时,表示队列为空,队尾指示器 rear 到达数组的上限处而 front 为-1 时,队列为满,如图 (c)所示。队尾指示器 rear 的值大于队头指示器 front 的值,队列中元素的个数可以由 rear-front 求得。由图 (d)可知,如果再有一个数据元素入队就会出现溢出。但事实上队列中并未满,还有空闲空间,把这种现象称为“假溢出” 。这是由于队列“队尾入队头出”的操作原则造成的。解决假溢出的方法是将顺序队列看成是首尾相接的循环结构,头尾指示器的关系不变,这种队列叫循环顺序队列( Circular sequenceQueue )。循环队列如图所示。

  当队尾指示器 rear 到达数组的上限时, 如果还有数据元素入队并且数组的第0 个空间空闲时,队尾指示器 rear 指向数组的 0 端。所以,队尾指示器的加 1 操作修改为:rear = (rear + 1) % maxsize。队头指示器的操作也是如此。当队头指示器 front 到达数组的上限时,如果还有数据元素出队,队头指示器 front 指向数组的 0 端。所以,队头指示器的加1 操作修改为:front = (front + 1) % maxsize。循环顺序队列操作示意图如图所示。

  由图可知,队尾指示器 rear 的值不一定大于队头指示器 front 的值,并且队满和队空时都有 rear=front。也就是说,队满和队空的条件都是相同的。解决这个问题的方法一般是少用一个空间, 如图 (d)所示, 把这种情况视为队满。所以,判断队空的条件是:rear==front,判断队满的条件是:(rear + 1) %maxsize==front。求循环队列中数据元素的个数可由(rear-front+maxsize)%maxsize公式求得。

  把循环顺序队列看作是一个泛型类,用数组来存储循环顺序队列中的元素,在类中用字段 data 来表示。用字段maxsize 表示循环顺序队列的容量, maxsize 的值可以根据实际需要修改,这通过类的构造器中的参数 size 来实现, 循环顺序队列中的元素由 data[0]开始依次顺序存放。字段 front 表示队头, front 的范围是 0 到 maxsize-1。 字段rear表示队尾, rear 的范围也是 0 到 maxsize-1。 如果循环顺序队列为空, front=rear=-1。当执行入队列操作时需要判断循环顺序队列是否已满,如果循环顺序队列已满,(rear + 1) % maxsize==front,循环顺序队列已满不能插入元素。

  循环顺序队列类 CSeqQueue<T>的实现说明如下所示。

public class CSeqQueue<T> : IQueue<T>//循环顺序队列类的实现
    {
        private int maxsize;
        private T[] data;
        private int front;
        private int rear;

        public int Maxsize
        {
            get { return maxsize; }
            set { maxsize = value; }
        }
        public int Front
        {
            get { return front; }
            set { front = value; }
        }
        public int Rear
        {
            get { return rear; }
            set { rear = value; }
        }
        public T this[int index]
        {
            get { return data[index]; }
            set { data[index] = value; }
        }
        public CSeqQueue(int size)
        {
            data = new T[size];
            maxsize = size;
            front = rear = -1;
        }
        /*
         循环顺序队列的长度取决于队尾指示器 rear 和队头指示器 front。一般情
        况下,rear 大于 front,因为入队的元素肯定比出队的元素多。特殊的情况是
        rear 到达数组的上限之后又从数组的低端开始,此时,rear 是小于 front 的。
        所以,rear 的大小要加上 maxsize。因此,循环顺序队列的长度应该是:
        (rear-front+maxsize)%maxsize。
         */
        public int GetLength()
        {
            return (rear - front + maxsize) % maxsize;
        }

        public bool IsEmpty()
        {
            return front == rear;
        }

        public void Clear()
        {
            rear = front = -1;
        }

        public bool IsFull()
        {
            return (rear + 1) % maxsize == front;
        }

        public void In(T item)
        {
            if (IsFull())
            {
                Console.WriteLine("Queue is full");
                return;
            }
            data[++rear] = item;
        }

        public T Out()
        {
            T tmp = default(T);
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty");
                return tmp;
            }
            tmp = data[++front];
            return tmp;
        }

        public T GetFront()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty!");
                return default(T);
            }
            return data[front + 1];
        }
    }

  2、链队列

  队列的另外一种存储方式是链式存储,这样的队列称为链队列(Linked Queue)。同链栈一样,链队列通常用单链表来表示,它的实现是单链表的简化。所以,链队列的结点的结构与单链表一样。由于链队列的操作只是在一端进行,为了操作方便,把队头设在链表的头部,并且不需要头结点。

  链队列结点类(Node<T>)的实现如下所示:

public class Node<T>//链队列结点类
    {
        private T data; //数据域
        private Node<T> next; //引用域
        //构造器
        public Node(T val, Node<T> p)
        {
            data = val;
            next = p;
        }
        //构造器
        public Node(Node<T> p)
        {
            next = p;
        }
        //构造器
        public Node(T val)
        {
            data = val;
            next = null;
        }
        //构造器
        public Node()
        {
            data = default(T);
            next = null;
        }
        //数据域属性
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        //引用域属性
        public Node<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    }

  把链队列看作一个泛型类,类中有两个字段 front 和 rear,表示队头指示器和队尾指示器。由于队列只能访问队头的数据元素,而链队列的队头指示器和队尾指示器又不能指示队列的元素个数,所以,与链栈一样,在类增设一个字段 num 表示链队列中结点的个数。

链队列类 LinkQueue<T>的实现说明如下所示。

public class LinkQueue<T> : IQueue<T>//链队列类的实现
    {
        private Node<T> front; //队列头指示器
        private Node<T> rear; //队列尾指示器
        private int num; //队列结点个数

        //队头属性
        public Node<T> Front
        {
            get { return front; }
            set { front = value; }
        }
        //队尾属性
        public Node<T> Rear
        {
            get { return rear; }
            set { rear = value; }
        }
        //队列结点个数属性
        public int Num
        {
            get { return num; }
            set { num = value; }
        }

        public LinkQueue()
        {
            front = rear = null;
            num = 0;
        }

        public int GetLength()
        {
            return num;
        }

        public bool IsEmpty()
        {
            return front == rear && num == 0;
        }

        public void Clear()
        {
            front = rear = null;
            num = 0;
        }

        public void In(T item)
        {
            Node<T> node = new Node<T>(item);
            if (rear == null)
            {
                rear = node;
            }
            rear.Next = node;
            rear = node;
            ++num;
        }

        public T Out()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty!");
                return default(T);
            }
            Node<T> node = front;
            front = front.Next;
            if (front == null)
            {
                rear = null;
            }
            --num;
            return front.Data;
        }

        public T GetFront()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty!");
                return default(T);
            }
            return front.Data;
        }
    }
时间: 2024-10-08 05:07:24

C#队列的相关文章

redis 学习 四 队列

<?php /** * redis实战 * * 利用列表list实现简单队列 * * @example php cache.php */ header('content-type:text/html;chaeset=utf-8'); $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); // 进队列 $userId = mt_rand(000000, 999999); $redis->rpush('QUEUE_NAME',j

构建队列,数组版本

队列作为基本的数据结构,是每个coder所必须掌握的. 队列在逻辑上就像是一条打饭的长队,排在前面的先打到饭(先进先出). 这里用一个数组用以构造一个队列,并设置两个指向,head指向队首,tail指向队尾,初始状态是head与tail指向同一位置(队列为空) 队列有两个操作:入队与出队. 1.入队:对比打饭排队的场景,新来的人排在后面,这是队尾tail需向后移一位. 2.出队:已经打好饭的人就可以出去了,这时队头也需向后移一位,让后面的人成为队头. 注意: 当head与tail都移到数组末端,

链队列代码及应用

链队列代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #define OK 1 #define ERROR 0 #define OVERFLOW -2 #define TRUE 1 #define FALSE 0 typedef int Status; typedef int ElemType; typedef struct Qnode{ int

caffe数据读取的双阻塞队列说明

caffe的datareader类中 class QueuePair { public: explicit QueuePair(int size); ~QueuePair(); BlockingQueue<T*> free_; BlockingQueue<T*> full_; DISABLE_COPY_AND_ASSIGN(QueuePair); }; 这个就是双阻塞队列,先将free队列填充到最大长度,然后按照如下规则: 1,每当生产者push时,先将full队列pop,如果fu

线性结构的常见应用之一 队列

定义:一种可以实现"先进先出"的存储结构 分类 链式队列 --  用链表实现 静态队列 --  用数组实现 静态队列通常都必须是循环队列 循环队列的讲解: 1.静态队列为什么必须是循环队列 2.循环队列需要几个参数来确定   需要两个参数来进行确定:front   rear 3.循环队列各个参数的含义 2个参数在不同的场合有不同的含义 建议初学者先记住,后面再想 1).队列初始化 front 和 rear 的值都是零 2).队列非空 front 代表的是队列的第一个元素 rear 代表

阻塞队列和生产者-消费者模式

阻塞队列提供了可阻塞的put和take方法.如果队列满了put将阻塞到有空间可用,如果队列为空,take将阻塞到有元素可用.队列可以是有界和无界的,无界的队列put将不会阻塞. 阻塞队列支持生产者消费者模式,该模式将找出需要完成的工作,和执行工作分开.生产者-消费者模式能简化开发过程,因为消除了生产者和消费者之间的代码依赖性,此外,该模式还将生产数据的过程和使用数据的过程解耦开来. 在基于阻塞队列构建的生产者-消费者设计中个,当数据生成时,生产者把数据放入队列,当消费者处理数据时,将从队列中获取

MQ队列管理器搭建(一)

多应用单MQ使用场景 如上图所示,MQ独立安装,或者与其中一个应用同处一机.Application1与Application2要进行通信,但因为跨系统,所以引入中间件来实现需求. Application1需要连接MQ,并将消息放入队列Queue中,Application2同样连接MQ,监听在Queue队列上,一旦发现有消息进入则取出该消息进行处理. 下面将给出创建队列管理器和队列的示例: 定义队列管理器名称为Qm1,本地队列名称为Queue,服务器连接通道CHAN_SERVER_CON,监听端口

PHP电商订单自动确认收货redis队列

一.场景 之前做的电商平台,用户在收到货之后,大部分都不会主动的点击确认收货,导致给商家结款的时候,商家各种投诉,于是就根据需求,要做一个订单在发货之后的x天自动确认收货.所谓的订单自动确认收货,就是在在特定的时间,执行一条update语句,改变订单的状态. 二.思路 最笨重的做法,通过linux后台定时任务,查询符合条件的订单,然后update.最理想情况下,如果每分钟都有需要update的订单,这种方式也还行.奈何平台太小,以及卖家发货时间大部分也是密集的,不会分散在24小时的每分钟.那么,

快速入门系列--WCF--06并发限流、可靠会话和队列服务

这部分将介绍一些相对深入的知识点,包括通过并发限流来保证服务的可用性,通过可靠会话机制保证会话信息的可靠性,通过队列服务来解耦客户端和服务端,提高系统的可服务数量并可以起到削峰的作用,最后还会对之前的事务知识做一定补充. 对于WCF服务来说,其寄宿在一个资源有限的环境中,为了实现服务性能最大化,需要提高其吞吐量即服务的并发性.然而在不进行流量控制的情况下,并发量过多,会使整个服务由于资源耗尽而崩溃.因此为相对平衡的并发数和系统可用性,需要设计一个闸门(Throttling)控制并发的数量. 由于

Python3-queue模块-同步队列

Python3中的queue模块实现多生产者,多消费者队列,特别适用于多个线程间的信息的安全交换,主要有三个类 queue.Queue(maxsize=0) 构造一个FIFO(先进先出)的队列 queue.LifoQueue(maxsize=0) 构造一个LIFO(后进先出)的队列 queue.PriorityQueue(maxsize=0) 构造一个具有优先级的队列,存储的是一个元组(n, value),n为数字代表优先级,数字越小,级别越高 这个模块定义了两个异常 queue.Empty 如