队列的实现:公式化描述

队列也是一种特殊的线性表。队列的插入和删除操作分别在线性表的两端进行,因此,队列是一个先进先出( first-in-first-out, FIFO)的线性表。

1、抽象数据类型

定义:队列( q u e n e)是一个线性表,其插入和删除操作分别在表的不同端进行。添加新元素的那一端被称为队尾 ( r e a r ),而删除元素的那一端被成为队首 ( f r o n t )。
所以,队列是一个先进先出( F I F O)的线性表,而堆栈是一个先进后出( L I F O)的线性表。

ADT:

2、公式化描述

队列可以用数组描述也可以用链表描述,此处先以数组即公式化的描述实现

第一种方式:location(i)=i-1

把数组q u e u e [ M a x S i z e ] 描述成一个队列,那么第一个元素为 q u e u e [ 0 ],第二个元素为 q u e u e [ 1 ],…。 f r o n t总是为 0, r e a r始终是最后一个元素的位置,队列的长度为 r e a r + 1 。对于一个空队列,有 r e a r =- 1 。

向队列中添加一个元素时,需要把 r e a r增1 ,并把新元素放入 q u e u e [ r e a r ]。这意味着一次添加操作所需要的时间为 O ( 1 )。删除一个元素时,把位置 1 至位置n的元素分别左移一个位置,因此删除一个元素所花费的时间为 O( n ),其中 n为删除完成之后队列中的元素数。如此看来,公式应用于堆栈,可使堆栈的插入和删除操作均耗时O ( 1 ),而应用于队列,则使队列的删除操作所需要的时间达到O ( n )。

第二种方式:
loacation(i)=location(1)+i-1
从队列中删除一个元素时,公式不要求把所有的元素都左移一个位置,只需简单地把location ( 1 )增加 1 即可。每次删除操作将导致 f r o n t右移一个位置。当 r e a r < M a x S i z e - 1 时才可以直接在队列的尾部添加新元素。若 r e a r = M a x S i z e - 1 且f r o n t > 0时(表明队列未满) ,为了能够继续向队列尾部添加元素,必须将所有元素平移到队列的左端(如图 6 - 4所示),以便在队列的右端留出空间。对于使用公式( 1 )的队列来说,这种平移操作将使最坏情况下的时间复杂性增加O( 1 ),而对于使用公式(2)的队列来说,最坏情况下的时间复杂性则增加了O ( n )。所以,使用公式(2)在提高删除操作执行效率的同时,却降低了添加操作的执行效率。

第三种方式:

location(i)=(location(1)+i-1)%MaxSize

队列的添加和删除操作在最坏情况下的时间复杂性均变成 O( 1 )。这时,用来描述队列的数组被视为一个环(如图 所示) 。在这种情况下,对 f r o n t的约定发生了变化,它指向队列首元素的下一个位置(逆时针方向) ,而 r e a r的含义不变。向图  a中的队列添加一个元素将得到图 b 所示的队列,而从图 b 的队列中删除一个元素则得到图c 所示的队列。

当且仅当 front=rear 时队列为空。初始条件f r o n t = r e a r = 0定义了一个初始为空的队列。现在需要确定队列为满的条件。如果不断地向图 6-5b 的队列添加元素,直到队列满为止,这时有 f r o n t = r e a r,竟然与队列为空的条件完全一样!因此,我们无法区分出队列是空还是满。为了避免这个问题,可以不允许队列被填满。为此,在向队列添加一个元素之前,先判断一下本次操作是否会导致队列被填满,如果是,则报错。因此,队列的最大容量实际上是 M a x S i z e - 1。

3、C++代码实现

队列定义:

  1 #ifndef ARRAYQUEUE_H
  2 #define ARRAYQUEUE_H
  3 #include <iostream>
  4 #include <new>
  5
  6 #include "exceptionerror.h"
  7 template<class T>
  8 class ArrayQueue
  9 {
 10
 11 public:
 12     ArrayQueue(const int &MaxQueueSize = 10);
 13     ~ArrayQueue()
 14     {
 15         if (queue!=NULL)
 16         {
 17             delete[] queue;
 18         }
 19      }
 20     bool IsEmpty()const{ return front == rear; }
 21     bool IsFull()const{ return (((rear + 1) % MaxSize == front) ? true : false); }
 22     T First()const;
 23     T Last()const;
 24     ArrayQueue<T>& Add(const T& x);
 25     ArrayQueue<T>& Delete(T& x);
 26     int Quality()const;//返回队列中的元素个数
 27     friend std::ostream& operator<<(std::ostream & output, const ArrayQueue<T>& q)
 28     {
 29         if (q.IsEmpty())
 30         {
 31             output << "queue is empty" << std::endl;
 32             return output;
 33         }
 34
 35         for (int i = (q.front + 1) % q.MaxSize; i <= q.rear; (++i) %= q.MaxSize)
 36         {
 37         output << q.queue[i] << " ";
 38         }
 39
 40         output << std::endl;
 41         return output;
 42     }
 43 private:
 44     int front;
 45     int rear;
 46     int MaxSize;
 47     T *queue;
 48 };
 49
 50 template<class T>
 51 ArrayQueue<T>::ArrayQueue(const int &MaxQueueSize=10):MaxSize(MaxQueueSize+1),front(0),rear(0)
 52 {
 53     if (MaxSize>1)
 54     {
 55         try
 56         {
 57             queue = new T[MaxSize];
 58         }
 59         catch (const std::bad_alloc& e)
 60         {
 61             std::cerr << "memory error" << std::endl;
 62         }
 63
 64     }
 65 }
 66
 67 template<class T>
 68 T ArrayQueue<T>::First()const
 69 {
 70     if (IsEmpty())
 71         throw OutofBounds();
 72
 73     return queue[(front + 1) % MaxSize];
 74 }
 75
 76 template<class T>
 77 T ArrayQueue<T>::Last()const
 78 {
 79     if (IsEmpty())
 80         throw OutofBounds();
 81
 82     return queue[rear%MaxSize];
 83 }
 84
 85 template<class T>
 86 ArrayQueue<T>& ArrayQueue<T>::Add(const T& x)
 87 {
 88     if (IsFull())
 89         throw NoMem();
 90     rear = (rear + 1) % MaxSize;
 91     queue[rear] = x;
 92     return *this;
 93 }
 94
 95 template<class T>
 96 ArrayQueue<T>& ArrayQueue<T>::Delete(T& x)
 97 {
 98     if (IsEmpty())
 99     {
100         throw OutofBounds();
101     }
102
103     front = (front + 1) % MaxSize;
104     x = queue[front];
105     return *this;
106 }
107
108 template<class T>
109 int ArrayQueue<T>::Quality()const
110 {
111     if (IsEmpty())
112     {
113         return 0;
114     }
115     else if (IsFull())
116     {
117         return MaxSize-1;
118     }
119
120     if ((front+1)%MaxSize<rear)
121     {
122         return rear - (front+1)%MaxSize+1;
123     }
124     else
125     {
126         return MaxSize-(front - rear);
127     }
128
129 }
130
131 #endif

exceptionerror.h定义:

 1 #ifndef OUTOFBOUND_H
 2 #define OUTOFBOUND_H
 3 #include <iostream>
 4 class OutofBounds
 5 {
 6 public:
 7     OutofBounds()
 8     {
 9         std::cerr << "Out of Bounds" << std::endl;
10         //std::exit(1);
11     }
12 };
13
14 class NoMem
15 {
16 public:
17     NoMem(){
18         std::cerr << "No Memory" << std::endl;
19         //std::exit(1);
20     }
21
22 };
23 #endif

测试:

 1 #include "ArrayQueue.h"
 2
 3 int main()
 4 {
 5     ArrayQueue<int> Q;
 6     Q.Add(1);
 7     Q.Add(2);
 8     Q.Add(1);
 9     Q.Add(2);
10     Q.Add(1);
11     Q.Add(2);
12     Q.Add(1);
13     Q.Add(2);
14     std::cout << Q;
15     std::cout << Q.Quality() << std::endl;
16     int x;
17     Q.Delete(x);
18     std::cout << Q;
19     std::cout << Q.Quality() << std::endl;
20     system("pause");
21     return 0;
22 }

输出:

时间: 2024-08-05 15:23:08

队列的实现:公式化描述的相关文章

堆栈的公式化描述实现

堆栈和队列可能是使用频率最高的数据结构,二者都来自于线性表数据结构(经过某种限制以后).堆栈数据结构是通过对线性表的插入和删除操作进行限制而得到的(插入和删除操作都必须在表的同一端完成),因此,堆栈是一个后进先出( last-in-first-out, LIFO)的数据结构.1.定义定义 [堆栈] 堆栈( s t a c k)是一个线性表,其插入(也称为添加)和删除操作都在表的同一端进行.其中一端被称为栈顶( t o p),另一端被称为栈底( b o t t o m). ADT: 2.公式化描述

二叉树实现:公式化描述

树的定义:树( t r e e) t 是一个非空的有限元素的集合,其中一个元素为根( r o o t),余下的元素(如果有的话)组成 t 的子树( s u b t r e e).树中层次最高的元素为根,其下一集的元素是余下元素所构成子树的根. 树的另一常用术语为级(level).指定树根的级为1. 元素的度(degree of an element)是指其孩子的个数.叶节点的度为0.树的度是其元素度的最大值. 二叉树的定义二叉树( binary tree) t 是有限个元素的集合(可以为空) .

【数据结构】之队列(Java语言描述)

在[这篇文章]中,我简单介绍了队列的基本数据结构及操作方式,并用C语言代码描述了队列的基本功能实现. JDK中默认为我们提供了队列的API-- Queue . Queue是一个接口,其中提供了处理队列及其操作的一些基本方法,如果我们想要创建自己的队列,就需要先创建一个类实现Queue接口. 在Java语言中也为我们提供了一些现成的Queue接口的实现类,如下: * @see java.util.Collection * @see LinkedList * @see PriorityQueue *

多线程十大经典案例之一 双线程读写队列数据

本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8646902 欢迎关注微博:http://weibo.com/MoreWindows 在<秒杀多线程系列>的前十五篇中介绍多线程的相关概念,多线程同步互斥问题<秒杀多线程第四篇一个经典的多线程同步问题>及解决多线程同步互斥的常用方法

秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8646902 欢迎关注微博:http://weibo.com/MoreWindows 在<秒杀多线程系列>的前十五篇中介绍多线程的相关概念,多线程同步互斥问题<秒杀多

FreeRTOS系列第18篇---FreeRTOS队列API函数

FreeRTOS为操作队列提供了非常丰富的API函数,包括队列的创建.删除,灵活的入队和出队方式.带中断保护的入队和出队等等.下面就来详细讲述这些API函数. 1.获取队列入队信息数目 1.1函数描述 UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); 返回队列中存储的信息数目.具有中断保护的版本为uxQueueMessagesWaitingFromISR(),原型为:UBaseType_t uxQueueMessagesWai

多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据

前十五篇中介绍多线程的相关概念,多线程同步互斥问题(第四篇)及解决多线程同步互斥的常用方法--关键段.事件.互斥量.信号量.读写锁.为了让大家更加熟练运用多线程,将会有十篇文章来讲解十个多线程使用案例,相信看完这十篇后会让你能更加游刃有余的使用多线程. 首先来看第一篇--第十六篇 多线程十大经典案例之一 双线程读写队列数据 <多线程十大经典案例之一双线程读写队列数据>案例描述: MFC对话框中一个按钮的响应函数实现两个功能:显示数据同时处理数据,因此开两个线程,一个线程显示数据(开了一个定时器

数据结构与算法JavaScript (二) 队列

队列是只允许在一端进行插入操作,另一个进行删除操作的线性表,队列是一种先进先出(First-In-First-Out,FIFO)的数据结构 队列在程序程序设计中用的非常的频繁,因为javascript单线程,所以导致了任何一个时间段只能执行一个任务,而且还参杂了异步的机制, 那么带来的问题: 1. 在异步操作执行的时候,同步代码还在继续,那么同步代码依赖异步,自然就会出错 2. 多个同步的任务在不同的时间段被调用     jQuery的动画中,我们经常写一段连续的动画代码 $book.anima

消息队列之 RabbitMQ

关于消息队列,从前年开始断断续续看了些资料,想写很久了,但一直没腾出空,近来分别碰到几个朋友聊这块的技术选型,是时候把这块的知识整理记录一下了. 市面上的消息队列产品有很多,比如老牌的 ActiveMQ.RabbitMQ ,目前我看最火的 Kafka ,还有 ZeroMQ ,去年底阿里巴巴捐赠给 Apache 的 RocketMQ ,连 redis 这样的 NoSQL 数据库也支持 MQ 功能.总之这块知名的产品就有十几种,就我自己的使用经验和兴趣只打算谈谈 RabbitMQ.Kafka 和 A