『壹』 在循環隊列中怎樣實現入隊和出隊操作 數據結構 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類型的函數。
『玖』 循環隊列基本操作求教
熟悉並能實現循環隊列的定義和基本操作