【C言語筆問題】Leetcode622 - 循環キューの設計

イベントアドレス: CSDN 21 日間学習チャレンジ

Leetcode622 - 循環キューの設計

トピックの説明

循環キューの実装を設計します。循環キューは線形データ構造であり、その動作は FIFO (先入れ先出し) 原理に基づいており、キューの最後尾がキューの先頭の後に接続されてループを形成します。「リングバッファ」とも呼ばれます。
循環キューの利点の 1 つは、このキューで以前に使用されていたスペースを利用できることです。通常のキューでは、キューがいっぱいになると、キューの先頭にまだ空きがある場合でも、次の要素を挿入できません。しかし、循環キューを使用すると、このスペースを新しい値の保存に使用できます。

リンク: https://leetcode.cn/problems/design-circular-queue

実装では次の操作をサポートする必要があります。

MyCircularQueue(k): コンストラクター。キューの長さを k に設定します。
フロント: キューの先頭から要素を取得します。キューが空の場合は -1 を返します。
後部: キューの最後にある要素を取得します。キューが空の場合は -1 を返します。
enQueue(value): 要素を循環キューに挿入します。挿入が成功した場合は true を返します。
deQueue(): 循環キューから要素を削除します。削除が成功した場合は true を返します。
isEmpty(): 循環キューが空かどうかを確認します。
isFull(): 循環キューがいっぱいかどうかを確認します。

:

MyCircularQueuecircularQueue = new MyCircularQueue(3); // 長さを 3 に設定
circularQueue.enQueue(1); // true を返します
circularQueue.enQueue(2); // true を返します
circularQueue.enQueue(3); // true を返します
circularQueue . enQueue(4); // false を返します、キューはいっぱいです
circularQueue.Rear(); // 3 を返します
circularQueue.isFull(); // true を返します
circularQueue.deQueue(); // true を返します
circularQueue.enQueue(4) ); // true を返します
circularQueue.Rear(); // 4 を返します


ヒント:

  • すべての値の範囲は 0 ~ 1000 です。
  • オペランドの範囲は 1 ~ 1000 です。
  • 組み込みのキュー ライブラリは使用しないでください。

コアコードパターン

typedef struct {
    
    

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    
    

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    

}

int myCircularQueueFront(MyCircularQueue* obj) {
    
    

}

int myCircularQueueRear(MyCircularQueue* obj) {
    
    

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    

}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    

}

void myCircularQueueFree(MyCircularQueue* obj) {
    
    

}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

アイデア分析とコード実装(C言語)

単一リンク リストの使用はあまり適切ではありません。たとえば、キュ​​ーの最後にある要素を取得したい場合は、再度それを走査する必要がありますが、キューの最後に添字 -1 を直接使用できます。また、単一リンクリストの作成はシーケンステーブルよりも面倒で、1つずつ使用する必要があり、ノードが作成され、すべてがリンクされます。動的シーケンス テーブルを使用することをお勧めします。
循環キュー内の要素の挿入または削除によってスペースは変更されず、上書き戦略が採用されます。実際、この質問の鍵は、空か満杯かを正確に判断することです。ここでは、後部がキューの最後にある要素の次の位置を指すようにすると、問題が発生します。この 2 つを区別することはできません。空と満杯の状態。なぜ?
図に示すように、キューの先頭ポインタとキューの末尾ポインタは両方とも、空の状態でも満杯の状態でも同じ位置を指します。
画像.png

解決策:

  1. カウントするサイズを増やす
  2. スペースを追加し、スペースがいっぱいになったら必ずスペースを残します。たとえば、循環キューに 4 つの要素がある場合、5 つのスペースが空きます。

後部の次の位置は前部であり、これが満杯状態であり、前部と後部が同じ位置を指している、これが空の状態である。ただし、これは論理構造の結論であり、図に示すように、論理構造のキューはいっぱいですが、循環キューを実現するために動的シーケンス テーブルを使用しますが、物理構造は別のものであることに注意してください。私たちの操作は物理学、構造に基づいていなければなりません。
画像.png
対処方法に注意したいのは、図のように、後ろの添え字+1%N、Nをスペースの総数(余分なスペースも含む)とし、その結果が前と等しいかどうかで判断することです。添字。
画像.png
構造体の宣言とキューの作成
循環リストは、動的シーケンス テーブルを使用して実装され、キューの先頭と末尾の添字と、動的シーケンス テーブルによって開かれるスペースの合計数 (もう 1 つ) を表す N を設定します。
動的シーケンス テーブルと同様に、ヒープ上のスペースを適用するキューを作成し、指定された k を超えるスペースを開き、初期化の完了後にキュー ポインターを返します。

typedef struct {
    
    
    int* arr;
    int front;
    int rear;
    int N;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    
    
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->arr = (int*)malloc((k+1)*sizeof(int));
    obj->front = obj->rear = 0;
    obj->N = k + 1;
    return obj;
}


と後ろが等しい場合はキューが空であることを意味し、判定がいっぱいである場合は、上記の 2 つの注意すべき状況があります。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    
    return obj->rear == obj->front;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    
    return ((obj->rear+1) % obj->N) == obj->front;
}

要素キュー
が満杯の場合は、要素の挿入は許可されず、 false が返されます。満杯でない場合は、要素は直接後方に配置されます。オブジェクトを待つことを忘れないでください。 >N. これは、後方が空間の端に到達したら添字0の位置を返せるように制御し、ループを実現するためです。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    
    if(myCircularQueueIsFull(obj))
        return false;
    obj->arr[obj->rear] = value;
    ++obj->rear;
    obj->rear %= obj->N;
    return true;
}

要素がキューの外にある場合は
、キューの外に失敗し false が返されます。要素が空でない場合は、先頭が 1 ビット前に移動します。obj->N を待つことを忘れないように注意してください。これは、後方がスペースの端に到達したら添え字0の位置に戻れるように制御し、ループを実現するためである。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return false;
    ++obj->front;
    obj->front %= obj->N;
    return true;
}

まずキューのヘッド要素を取得して
キューが空かどうかを確認し、空の場合は -1 を返し、空でない場合はキューのヘッド要素を直接返します。

int myCircularQueueFront(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->arr[obj->front];
}

キューの最後尾の要素を取得するには、
まずキューが空かどうかを確認します。空の場合は -1 を返します。空でない場合は、後部が 0 である場合と後部が 0 ではない場合の 2 つの状況に注意する必要があります。 0.
次の 2 つの書き方があります。

int myCircularQueueRear(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else if(obj->rear == 0)
        return obj->arr[obj->N - 1];
    else
        return obj->arr[obj->rear - 1];
}

queue を破棄して解放すること
で動的に作成される構造が 2 つあり、1 つはキュー構造、もう 1 つは動的配列です。最初に配列を解放し、次に構造を解放する必要があります。

void myCircularQueueFree(MyCircularQueue* obj) {
    
    
    free(obj->arr);
    obj->arr = NULL;
    obj->front = obj->rear = obj->N = 0;
    free(obj);
}

ここに画像の説明を挿入

Supongo que te gusta

Origin blog.csdn.net/weixin_61561736/article/details/126418702
Recomendado
Clasificación