I.概要
キューの特別な構造のため、通常、前のセクションのようにチェーンリストを使用します。ただし、シーケンス構造にはシーケンス構造の利点がある場合があるため、シーケンステーブルを使用して何らかの方法でリストを実装することもできます。これは、循環キューによって実現されます。
2.原則の実現
1.ノード
これはシーケンシャル構造であるため、ノードは他のものを必要とせず、データを格納するだけで済みます。
2.循環キュー構造
循環キューの特別なメカニズムのため、通常、キューの拡張は考慮されません。次に、固定長にする必要がありMaxSize
ます。3つの部分で構成されたキューので基址*base
、front头指针
、tail尾指针
。ループであるため、テールポインタの値がヘッドポインタの前にある可能性があります。たとえばMaxSize=6 front=4 tail=2
、キューの長さは4 5 0 1
これらの4つの位置である必要があるため、計算すると、になります(tail-front+MaxSize)%Maxsize
。
3.キューの初期化と空のキューの判断
キューが初期化され、*base
ストレージスペースが動的に直接生成されてから、ストレージスペースが生成されfront=tail=0
ます。空のキューの場合は、そのためですfront=tail
。
4.キューがいっぱいであるという判断
私たちのキューはいっぱいです、最初の終わりの前に2つのケースがあります:tail=MaxSize-1
、;最初front=0
の終わりの前にあるケースの関数があります:tail+1=front
。したがって、要約すると、その判断はである必要があります(tail+1)%MaxSize=front
。
5.キューの登録
チームに参加するにはtail
、値を挿入した場所を書き込むだけで、状況++tail
が発生する可能性があるtail+1=MaxSize
ため、1つを使用するif
か计算
、その値を変更する必要があります。個人的には計算を使用することをお勧めしますtail = (tail+1)%MaxSize
。しかし、理論的には、ifを使用して更新しても問題はありません。ただし、快慢指针
この方法を使用して問題を解決する場合もあるため、この計算方法のフォールトトレランス率ははるかに優れている可能性があります。
しかし、我々はキューが満杯であるかどうかをまず判断し、キューに格納されている要素の最大数がなる場合なおMaxsize-1
ので、tail
それは常にある尾元
位置、それが直接利用可能である、との判断がいっぱいですtail+1=front
最後の要素を使用することはできませんので、 。次の図に示すように、チームが満員であると判断する方法を変更しない限り、J8は実際にはデータがないはずだからです。
6.デキュー
デキューはエンキューに似ていますが、順序構造のアドレスに対して何もする必要はなく、ヘッドポインタを配置するだけで済みますfront+1
。もちろん、MaxSize
を超える場合もあるため、いくつかのフォールトトレランスメカニズムを採用する必要があります。front = (front+1)%MaxSize
3.コードの実装
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
#define OverFlow 2
#define MaxSize 5
typedef struct dataType{
int data;
}dataType;
typedef struct cqueue{
dataType *base;
int front;
int tail;
}cqueue;
cqueue* CqueueInit(void);
int CqueuePush(cqueue* q, dataType data);
int CqueueGet(cqueue *q, dataType *data);
int CqueuePop(cqueue *q);
int CqueueShow(cqueue *q);
int main()
{
// 主函数随便改
cqueue *q = CqueueInit();
dataType test;
test.data = 0;
CqueuePush(q,test);
test.data = 1;
CqueuePush(q,test);
test.data = 2;
CqueuePush(q,test);
test.data = 3;
CqueuePush(q,test);
CqueuePop(q);
test.data = 4;
CqueuePush(q,test);
CqueueShow(q);
return 0;
}
cqueue* CqueueInit(void)
{
cqueue *q = (cqueue*)malloc(sizeof(cqueue));
dataType *data = (dataType*)malloc(sizeof(dataType)*MaxSize);
if (q != NULL && data != NULL)
{
q->base = data;
q->front = 0;
q->tail = 0; // 初始化循环队列
return q;
}
exit(0);
}
int CqueuePush(cqueue* q, dataType data)
{
if ((q->tail+1)%MaxSize == q->front) return OverFlow;
// 注意这样的写法会导致最后有一个节点存不到数据
*(q->base+q->tail) = data;
q->tail += 1;
return OK;
}
int CqueueGet(cqueue *q, dataType *data)
{
if ((q->tail - q->front)%MaxSize == 0)
{
return ERROR;
}
else
{
*data = *(q->base+q->tail); // 返回头指针指向的值
return OK;
}
}
int CqueuePop(cqueue *q)
{
q->front += 1; // 更新一下指向的元素
return OK;
}
int CqueueShow(cqueue *q) // 这个函数就打印一下队列 看看效果
{
int start,end = 0;
int cont = 0;
if (q->tail < q->front) // 队尾在队头的前面
{
end = q->tail + MaxSize;
}
else
{
end = q->tail; // 队头在队尾的前面
}
for(start = q->front; start < end; ++start)
{
printf("No.%d data is %d\n", cont, (q->base+start)->data);
++cont;
}
}