目次
662. 循環キューを設計する
622. 循環キューの設計 - LeetCodehttps://leetcode.cn/problems/design-circular-queue/
トピック
循環キューの実装を設計します。循環キューは線形データ構造であり、その動作は FIFO (先入れ先出し) 原理に基づいており、キューの最後尾がキューの先頭の後に接続されてループを形成します。 「リングバッファ」とも呼ばれます。
循環キューの利点の 1 つは、このキュー内で以前に使用されていたスペースを利用できることです。通常のキューでは、キューがいっぱいになると、キューの先頭にまだスペースがある場合でも、次の要素を挿入できません。しかし、循環キューを使用すると、このスペースを新しい値の保存に使用できます。
実装では次の操作をサポートする必要があります。
MyCircularQueue(k)
: コンストラクター、キューの長さを k に設定します。Front
: キューの先頭から要素を取得します。キューが空の場合は、-1 が返されます。Rear
: キューの末尾要素を取得します。キューが空の場合は、-1 が返されます。enQueue(value)
: 要素を循環キューに挿入します。挿入が成功した場合は true を返します。deQueue()
: 循環キューから要素を削除します。削除が成功した場合は true を返します。isEmpty()
: 循環キューが空かどうかを確認してください。isFull()
: 循環キューがいっぱいかどうかを確認します。
例:
アイデア
k 個の有効な要素を格納できるサイズ k+1 の配列を開きます。キュー フロントの先頭とキュー リアの後部が配列の添字です。データの挿入と削除中に前部と後部の位置が移動します。配列の終端を超えると配列の先頭位置に戻り、ループを形成して循環キューの効果を実現します。
循環キューを実装すると、次のような効果が得られます。
難易度1:
rear は配列の末尾にデータを挿入します:rear が配列の末尾に到達したら、データを後部に挿入する必要があり、rear は配列の先頭にループバックする必要があります。直接後方ではなく配列++ 以上です。
解決:
- モジュロのアイデア: 以下に示すように、rear++ は配列の長さ k+1 を変調し、配列の末尾を超えた後に配列の先頭に戻ります。
難易度2:
フロントが配列の最後にある場合はデータを削除します。フロントが配列の最後にある場合は、キューの先頭から要素を削除する必要があります。先頭が配列の末尾を越えて移動されます。
解決:
- モジュロのアイデア: 以下に示すように、フロントが配列の末尾を超えると、配列の長さ k+1 をモジュロして、フロントは配列の先頭に戻ります。
難易度3:
キューの末尾要素を取得する: 後部が配列インデックス 0 の位置にある場合、後部は -1 ~ -1 の位置にあり、末尾要素は配列の末尾の位置。
解決:
- if文を追加し、rearが添字0の位置にあるとき、キューの末尾要素の位置を添字k+1とします。
- モジュロのアイデア: 以下に示すように添え字 (rear+(k+1)-1)%(k+1) を削除する場合:
コード
(次の関数には、サウンディングやフルサウンディングなど、呼び出される関数が含まれています。これらの関数は前に配置する必要があります。最初に宣言してから使用してください。)
typedef struct {
int* a;//起始地址
int front;//数组下标
int rear;//数组下标
int k;//有效数据个数
} MyCircularQueue;
//构造器
MyCircularQueue* myCircularQueueCreate(int k) {
//结构体开辟空间
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//数组开辟空间,多开辟一个可区分满和空
obj->a=(int*)malloc(sizeof(int)*(k+1));
//开始是空的状态
obj->front=obj->rear=0;
//传入的数(有效个数)给给k
obj->k=k;
return obj;
}
//探空和探满尽量位置往前放
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front==obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear+1)%(obj->k+1)==(obj->front);
}
//插入一个元素 成功返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//判断是否满
if(myCircularQueueIsFull(obj))
return false;
//插入rear位置
obj->a[obj->rear]=value;
obj->rear++;
//模上一个数组的长度,rear超过到数组尾可以循环回到数组头
obj->rear%=(obj->k+1);
return true;
}
//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//判断是否为空
if(myCircularQueueIsEmpty(obj))
return false;
//队头front往后移 ++front
++obj->front;
//取模可在超过队尾时回到队头,取模不影响中间的移动
obj->front%=(obj->k+1);
return true;
}
//取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {
//探空
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
//模上k+1 rear+(k+1)-1 % (k+1)
return obj->a[(obj->rear+obj->k) % (obj->k+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}