‘壹’ 在循环队列中怎样实现入队和出队操作 数据结构 C语言
入队操作
功能:将元素 x 插入到Q的队尾。
算法:Status EnQueue(SqQueue &Q, QElemType e) {
if ((Q.rear+1) % MaxQsize == Q.front) return ERROR; // 队列满
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1) % MaxQsize;
return OK;
}
出队操作
功能:删除Q的队头元素,并返回其值。
算法: Status DeQueue(SqQueue &Q, QElemType &e) {
if (Q.front == Q. rear) return ERROR; // 队列空
e = Q.base[Q.front];
Q.front=(Q.front+1) % MaxQsize;
return OK;
}
‘贰’ 循环队列的基本操作
#define MaxSize 5
#include<stdio.h>
#include<malloc.h>
struct queue{
int qu[MaxSize];
int front;
int rear;
int tag; //front==rear时表示队列满或空的标志:tag==1表示满,tag==0表示空
};
struct queue *InitQu()
{struct queue *q;
q=(struct queue *)malloc(sizeof(struct queue));//分配空间
q->front=0;
q->rear=0;
q->tag=0;
return q;
}
int EnQu(struct queue *q,int x)
{
if(q->rear==q->front && q->tag==1)//表示队列已满
return 0;
else
{ q->qu[q->rear]=x;
q->rear=(q->rear+1)%MaxSize; //尾指针向后走一步
if(q->rear==q->front) //若有元素入队以后出现q->rear==q->front,则表示队列满
q->tag=1;
return 1;
}
}
int DeQu(struct queue *q)
{
if(q->rear==q->front && q->tag==0)//表示队空
return 0;
else
{ q->qu[q->front]=0;//将队头元素的值赋值为0
q->front=(q->front+1)%MaxSize;//头指针向后走一步
if(q->rear==q->front)//若有元素出队以后出现q->rear==q->front,则表示队列空
q->tag=0;
return 1;
}
}
void Display(struct queue *q)
{int n,i;//n为队列中元素的个数
if(q->rear==q->front && q->tag==1)//队列满
n=MaxSize;
else
n=(q->rear-q->front+MaxSize)%MaxSize;
for(i=0;i<n;i++)
printf("%4d",q->qu[(q->front+i)%MaxSize]);
printf("\n");
}
main()
{struct queue *q;
int c,k,x;
q=InitQu();
loop:
printf("---------(1).入队列请按数字键1.---------------");
printf("\n---------(2).出队列请按数字键2.---------------");
printf("\n---------(3).打印队列请按数字键3.-------------");
printf("\n请选择要进行的操作:");
scanf("%d",&c);
switch(c)
{ case 1:{
do{
printf("请输入入队元素的值:");
scanf("%d",&x);
EnQu(q,x);
printf("\n是否继续入队,是请按数字键1,否则请按其他数字键!");
scanf("%d",&k);
}while(k==1);
goto loop;
break;
}
case 2:{
do{
DeQu(q);
printf("\n是否继续出队,是请按数字键1,否则请按其他数字键!");
scanf("%d",&k);
}while(k==1);
goto loop;
break;
}
case 3:{ Display(q);
goto loop;
break;
}
}
printf("\n");
}
‘叁’ 栈 实现循环队列上的六种基本操作 (初始化、入队、出队、求队列元素个数、判断队列是否为空、为满)
#include <iostream>
using namespace std;
const int queuesize=30;
template <class T>
class crilink
{
T m_data[queuesize];
int m_front;
int m_rear;
public:
crilink();
~crilink();
void enter(T e);
T leave();
T getfront();
bool isempty();
bool isfull();
int length();
};
template <class T>
crilink<T>::crilink()
{
m_front=m_rear=0;
}
template <class T>
crilink<T>::~crilink()
{}
template <class T>
void crilink<T>::enter(T e)
{
if((m_rear+1)%queuesize==m_front) throw "上溢";
m_data[m_rear]=e;
m_rear=(m_rear+1)%queuesize;
}
template <class T>
T crilink<T>::leave()
{
T e;
if(m_front==m_rear)throw "下溢";
e=m_data[m_front];
m_front=(m_front+1)%queuesize;
return e;
}
template <class T>
T crilink<T>::getfront()
{
if(m_front==m_rear)throw "下溢";
return m_data[m_front];
}
template <class T>
bool crilink<T>::isempty()
{
if(m_front==m_rear) return true;
return false;
}
template <class T>
bool crilink<T>::isfull()
{
if((m_rear+1)%queuesize==m_front) return true;
return false;
}
template <class T>
int crilink<T>::length()
{
return (m_rear-m_front+queuesize)%queuesize;
}
int main()
{
crilink<int> h;
int i,n;
cin>>n;
for(i=1;i<=n;i++)
{
h.enter(i);
}
while(!h.isempty())
{
cout<<h.leave()<<endl;
if(!h.isempty())
h.enter(h.leave());
}
return 0;
}
‘肆’ 在循环队列中入队、出队操作的过程
入队:
1、新建一个变量p,指定内存空间;
2、将变量p的next指针指向队头head;
3、将队尾变量的next指针指向变量p;
4、将变量p变为队尾(或队头);
5、具体如下:
new(p);
p^.data:=****(自选数据)
p^.next:=head;
tail^.next:=p;
tail:=tail^.next;(或head:=p;)
出队:单项循环队列中需要搜索出队变量的前缀(或后缀),双向循环队列不需,设该出队变量为x;前缀为p,后缀o为q;
1、将前缀的next(或right)指针指向后缀;
2、(单项循环队列不要此项)将后缀的last(或left)指针指向前缀;
3、若从队头或队尾出队则要调整队头变量head或队尾变量tail;
4、释放出队的变量;
5、具体如下:
p^.next:=q;
q^.last:=p;
head:=q;(或tail:=p;)
dispose(x);
‘伍’ 采用顺序存储如何实现循环队列的初始化、入队、出队操作
#include<stdio.h>
#define MAXSIZE 100
typedef struct seqqueue
{
int data[MAXSIZE];
int front;
int rear;
}seqqueue;
void Initseqqueue(seqqueue &q) //循环队列初始化
{
q.front =q.rear=0;
printf("初始化成功!\n");
}
int enqueue(seqqueue &q,int e) //数据元素e入队列
{
if((q.rear+1)%MAXSIZE==q.front)
{
printf("循环队列满!\n");
return 0;
}
else
{
q.data[q.rear]=e;
q.rear=(q.rear+1)%MAXSIZE;
printf("%d入队列成功!\n",e);
return 1;
}
}
int isemptyqueue(seqqueue &q) //判断循环队列是否为空
{
if(q.rear ==q.front )
{
printf(" 空队列!\n");
return 1;
}
else
{
printf("非空队列!\n");
return 0;
}
}
int dequeue(seqqueue &q,int &e) //数据元素出队列,出队列元素暂存储于e中
{
if(!isemptyqueue(q))
{
e=q.data [q.front ];
q.front =(q.front +1)%MAXSIZE;
printf("出队列成功!\n");
return 1;
}
else
{
printf("出队列失败!\n");
return 0;
}
}
void main()
{
int x=0;
seqqueue qa;
Initseqqueue(qa);
isemptyqueue(qa);
dequeue(qa,x);
enqueue(qa,25);
isemptyqueue(qa);
dequeue(qa,x);
}
‘陆’ 如何在后台部署深度学习模型
搭建深度学习后台服务器
我们的Keras深度学习REST API将能够批量处理图像,扩展到多台机器(包括多台web服务器和Redis实例),并在负载均衡器之后进行循环调度。
为此,我们将使用:
KerasRedis(内存数据结构存储)
Flask (python的微web框架)
消息队列和消息代理编程范例
本篇文章的整体思路如下:
我们将首先简要讨论Redis数据存储,以及如何使用它促进消息队列和消息代理。然后,我们将通过安装所需的Python包来配置Python开发环境,以构建我们的Keras深度学习REST API。一旦配置了开发环境,就可以使用Flask web框架实现实际的Keras深度学习REST API。在实现之后,我们将启动Redis和Flask服务器,然后使用cURL和Python向我们的深度学习API端点提交推理请求。最后,我们将以对构建自己的深度学习REST API时应该牢记的注意事项的简短讨论结束。
第一部分:简要介绍Redis如何作为REST API消息代理/消息队列
测试和原文的命令一致。
第三部分:配置Python开发环境以构建Keras REST API
文章中说需要创建新的虚拟环境来防止影响系统级别的python项目(但是我没有创建),但是还是需要安装rest api所需要依赖的包。以下为所需要的包。
第四部分:实现可扩展的Keras REST API
首先是Keras Redis Flask REST API数据流程图
让我们开始构建我们的服务器脚本。为了方便起见,我在一个文件中实现了服务器,但是它可以按照您认为合适的方式模块化。为了获得最好的结果和避免复制/粘贴错误,我建议您使用本文的“下载”部分来获取相关的脚本和图像。
为了简单起见,我们将在ImageNet数据集上使用ResNet预训练。我将指出在哪里可以用你自己的模型交换ResNet。flask模块包含flask库(用于构建web API)。redis模块将使我们能够与redis数据存储接口。从这里开始,让我们初始化将在run_keras_server.py中使用的常量.
我们将向服务器传递float32图像,尺寸为224 x 224,包含3个通道。我们的服务器可以处理一个BATCH_SIZE = 32。如果您的生产系统上有GPU(s),那么您需要调优BATCH_SIZE以获得最佳性能。我发现将SERVER_SLEEP和CLIENT_SLEEP设置为0.25秒(服务器和客户端在再次轮询Redis之前分别暂停的时间)在大多数系统上都可以很好地工作。如果您正在构建一个生产系统,那么一定要调整这些常量。
让我们启动我们的Flask app和Redis服务器:
在这里你可以看到启动Flask是多么容易。在运行这个服务器脚本之前,我假设Redis服务器正在运行(之前的redis-server)。我们的Python脚本连接到本地主机6379端口(Redis的默认主机和端口值)上的Redis存储。不要忘记将全局Keras模型初始化为None。接下来我们来处理图像的序列化:
Redis将充当服务器上的临时数据存储。图像将通过诸如cURL、Python脚本甚至是移动应用程序等各种方法进入服务器,而且,图像只能每隔一段时间(几个小时或几天)或者以很高的速率(每秒几次)进入服务器。我们需要把图像放在某个地方,因为它们在被处理前排队。我们的Redis存储将作为临时存储。
为了将图像存储在Redis中,需要对它们进行序列化。由于图像只是数字数组,我们可以使用base64编码来序列化图像。使用base64编码还有一个额外的好处,即允许我们使用JSON存储图像的附加属性。
base64_encode_image函数处理序列化。类似地,在通过模型传递图像之前,我们需要反序列化图像。这由base64_decode_image函数处理。
预处理图片
我已经定义了一个prepare_image函数,它使用Keras中的ResNet50实现对输入图像进行预处理,以便进行分类。在使用您自己的模型时,我建议修改此函数,以执行所需的预处理、缩放或规范化。
从那里我们将定义我们的分类方法
classify_process函数将在它自己的线程中启动,我们将在下面的__main__中看到这一点。该函数将从Redis服务器轮询图像批次,对图像进行分类,并将结果返回给客户端。
在model = ResNet50(weights="imagenet")这一行中,我将这个操作与终端打印消息连接起来——根据Keras模型的大小,加载是即时的,或者需要几秒钟。
加载模型只在启动这个线程时发生一次——如果每次我们想要处理一个映像时都必须加载模型,那么速度会非常慢,而且由于内存耗尽可能导致服务器崩溃。
加载模型后,这个线程将不断轮询新的图像,然后将它们分类(注意这部分代码应该时尚一部分的继续)
在这里,我们首先使用Redis数据库的lrange函数从队列(第79行)中获取最多的BATCH_SIZE图像。
从那里我们初始化imageIDs和批处理(第80和81行),并开始在第84行开始循环队列。
在循环中,我们首先解码对象并将其反序列化为一个NumPy数组image(第86-88行)。
接下来,在第90-96行中,我们将向批处理添加图像(或者如果批处理当前为None,我们将该批处理设置为当前图像)。
我们还将图像的id附加到imageIDs(第99行)。
让我们完成循环和函数
在这个代码块中,我们检查批处理中是否有图像(第102行)。如果我们有一批图像,我们通过模型(第105行)对整个批进行预测。从那里,我们循环一个图像和相应的预测结果(110-122行)。这些行向输出列表追加标签和概率,然后使用imageID将输出存储在Redis数据库中(第116-122行)。
我们使用第125行上的ltrim从队列中删除了刚刚分类的图像集。最后,我们将睡眠设置为SERVER_SLEEP时间并等待下一批图像进行分类。下面我们来处理/predict我们的REST API端点
稍后您将看到,当我们发布到REST API时,我们将使用/predict端点。当然,我们的服务器可能有多个端点。我们使用@app。路由修饰符以第130行所示的格式在函数上方定义端点,以便Flask知道调用什么函数。我们可以很容易地得到另一个使用AlexNet而不是ResNet的端点,我们可以用类似的方式定义具有关联函数的端点。你懂的,但就我们今天的目的而言,我们只有一个端点叫做/predict。
我们在第131行定义的predict方法将处理对服务器的POST请求。这个函数的目标是构建JSON数据,并将其发送回客户机。如果POST数据包含图像(第137和138行),我们将图像转换为PIL/Pillow格式,并对其进行预处理(第141-143行)。
在开发这个脚本时,我花了大量时间调试我的序列化和反序列化函数,结果发现我需要第147行将数组转换为C-contiguous排序(您可以在这里了解更多)。老实说,这是一个相当大的麻烦事,但我希望它能帮助你站起来,快速跑。
如果您想知道在第99行中提到的id,那么实际上是使用uuid(通用唯一标识符)在第151行生成的。我们使用UUID来防止hash/key冲突。
接下来,我们将图像的id和base64编码附加到d字典中。使用rpush(第153行)将这个JSON数据推送到Redis db非常简单。
让我们轮询服务器以返回预测
我们将持续循环,直到模型服务器返回输出预测。我们开始一个无限循环,试图得到157-159条预测线。从这里,如果输出包含预测,我们将对结果进行反序列化,并将结果添加到将返回给客户机的数据中。我们还从db中删除了结果(因为我们已经从数据库中提取了结果,不再需要将它们存储在数据库中),并跳出了循环(第163-172行)。
否则,我们没有任何预测,我们需要睡觉,继续投票(第176行)。如果我们到达第179行,我们已经成功地得到了我们的预测。在本例中,我们向客户机数据添加True的成功值(第179行)。注意:对于这个示例脚本,我没有在上面的循环中添加超时逻辑,这在理想情况下会为数据添加一个False的成功值。我将由您来处理和实现。最后我们称烧瓶。jsonify对数据,并将其返回给客户端(第182行)。这就完成了我们的预测函数。
为了演示我们的Keras REST API,我们需要一个__main__函数来实际启动服务器
第186-196行定义了__main__函数,它将启动classify_process线程(第190-192行)并运行Flask应用程序(第196行)。
第五部分:启动可伸缩的Keras REST API
要测试我们的Keras深度学习REST API,请确保使用本文的“下载”部分下载源代码示例图像。从这里,让我们启动Redis服务器,如果它还没有运行:
然后,在另一个终端中,让我们启动REST API Flask服务器:
另外,我建议在向服务器提交请求之前,等待您的模型完全加载到内存中。现在我们可以继续使用cURL和Python测试服务器。
第七部分:使用cURL访问Keras REST API
使用cURL来测试我们的Keras REST API服务器。这是我的家庭小猎犬Jemma。根据我们的ResNet模型,她被归类为一只拥有94.6%自信的小猎犬。
你会在你的终端收到JSON格式的预测:
第六部分:使用Python向Keras REST API提交请求
如您所见,使用cURL验证非常简单。现在,让我们构建一个Python脚本,该脚本将发布图像并以编程方式解析返回的JSON。
让我们回顾一下simple_request.py
我们在这个脚本中使用Python请求来处理向服务器提交数据。我们的服务器运行在本地主机上,可以通过端口5000访问端点/predict,这是KERAS_REST_API_URL变量(第6行)指定的。
我们还定义了IMAGE_PATH(第7行)。png与我们的脚本在同一个目录中。如果您想测试其他图像,请确保指定到您的输入图像的完整路径。
让我们加载图像并发送到服务器:
我们在第10行以二进制模式读取图像并将其放入有效负载字典。负载通过请求发送到服务器。在第14行发布。如果我们得到一个成功消息,我们可以循环预测并将它们打印到终端。我使这个脚本很简单,但是如果你想变得更有趣,你也可以使用OpenCV在图像上绘制最高的预测文本。
第七部分:运行简单的请求脚本
编写脚本很容易。打开终端并执行以下命令(当然,前提是我们的Flask服务器和Redis服务器都在运行)。
使用Python以编程方式使用我们的Keras深度学习REST API的结果
第八部分:扩展深度学习REST API时的注意事项
如果您预期在深度学习REST API上有较长一段时间的高负载,那么您可能需要考虑一种负载平衡算法,例如循环调度,以帮助在多个GPU机器和Redis服务器之间平均分配请求。
记住,Redis是内存中的数据存储,所以我们只能在队列中存储可用内存中的尽可能多的图像。
使用float32数据类型的单个224 x 224 x 3图像将消耗602112字节的内存。
‘柒’ 用链表实现一个循环队列 以及其所应该有的操作
// 单链队列-队列的链式存储结构。
typedef struct QNode
{ QElemType data;
QNode *next;
}*QueuePtr;
struct LinkQueue
{ QueuePtr front,rear;
};
// 链队列的基本操作(9个)
void InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
if(!Q.front)
exit(OVERFLOW);
Q.front->next=NULL;
}
void DestroyQueue(LinkQueue &Q)
{
while(Q.front)
{ Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
}
}
void ClearQueue(LinkQueue &Q)
{
DestroyQueue(Q);
InitQueue(Q);
}
Status QueueEmpty(LinkQueue Q)
{
if(Q.front->next==NULL)
return TRUE;
else
return FALSE;
}
int QueueLength(LinkQueue Q)
{
int i=0;
QueuePtr p=Q.front;
while(Q.rear!=p)
{ i++;
p=p->next;
}
return i;
}
Status GetHead(LinkQueue Q,QElemType &e)
{
QueuePtr p;
if(Q.front==Q.rear)
return ERROR;
p=Q.front->next;
e=p->data;
return OK;
}
void EnQueue(LinkQueue &Q,QElemType e)
{
QueuePtr p;
p=(QueuePtr)malloc(sizeof(QNode));
if(!p)
exit(OVERFLOW);
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
}
Status DeQueue(LinkQueue &Q,QElemType &e)
{
QueuePtr p;
if(Q.front==Q.rear)
return ERROR;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
return OK;
}
void QueueTraverse(LinkQueue Q,void(*visit)(QElemType))
{
QueuePtr p=Q.front->next;
while(p)
{ visit(p->data);
p=p->next;
}
printf("\n");
}
// 链队列的主函数
void main()
{
int i;
QElemType d;
LinkQueue q;
InitQueue(q);
printf("成功地构造了一个空队列\n");
printf("是否空队列?%d(1:空 0:否),",QueueEmpty(q));
printf("队列的长度为%d\n",QueueLength(q));
EnQueue(q,-5);
EnQueue(q,5);
EnQueue(q,10);
printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q));
printf("是否空队列?%d(1:空 0:否),",QueueEmpty(q));
printf("队列的元素依次为");
QueueTraverse(q,print);
i=GetHead(q,d);
if(i==OK)
printf("队头元素是%d,",d);
DeQueue(q,d);
printf("删除了队头元素%d,",d);
i=GetHead(q,d);
if(i==OK)
printf("新的队头元素是%d\n",d);
ClearQueue(q);
printf("清空队列后,q.front=%u,q.rear=%u,q.front->next=%u\n",q.front,
q.rear,q.front->next);
DestroyQueue(q);
printf("销毁队列后,q.front=%u,q.rear=%u\n",q.front,q.rear);
}
希望对你有帮助
‘捌’ 循环队列的定义及其相关操作算法的实现
malloc这个函数的头文件你忘记包含了,在文件的开始处添加如下代码即可:
#include "malloc.h"
另外你的主函数main的返回类型没有给出,缺省时作为int型的返回值,但你的main中无返回一个int型值的语句,程序会给出一个警告,因此你最好将main函数设计为void类型的函数。
‘玖’ 循环队列基本操作求教
熟悉并能实现循环队列的定义和基本操作