目次
2. 配列循環キューのヒープ領域メモリ アプリケーション インターフェイス
2. 静的な一方向循環リンク リストのインターフェイスを作成する
問題のソース: 622. 円形キューの設計 - Leetcode
1. 循環キューの簡単な紹介
- 循環キューは一般に、データが先入れ先出しの原則に準拠する静的線形データ構造です。
- 循環キューのコンテナーの先頭アドレスとコンテナーの末尾アドレスは、特定の操作 (ポインターのリンク、配列の添字など) によって接続されるため、コンテナー空間の再利用が実現されます(非循環静的キューでは、キューがいっぱいになると、キューの先頭にまだスペースがあるにもかかわらず、次の要素を挿入することはできません)
2.静的配列で循環キューを実装する
キューの構造を維持します。
typedef struct { int * arry; //指向堆区数组的指针 int head; //队头指针 int tail; //队尾指针(指向队尾数据的下一个位置)(不指向有效数据) int capacity;//静态队列的容量 } MyCircularQueue;
1. アレイ循環キュー構造の設計
静的配列の容量が k ( k 個のデータを格納できる)であると仮定します。
- キューの基本的なデータ構造によると、配列内の有効なデータ空間を維持するために使用される2 つのポインター、つまりヘッド ポインターとテール ポインターがあり、ヘッド ポインターはキューの先頭にあるデータを指すために使用されます。 、および末尾は、キューの末尾にあるデータの次の位置を指すために使用されます(つまり、末尾ポインタは有効なデータを指しません)。
- 図に示すように、有効なデータのメモリ空間は、ヘッド ポインタとテール ポインタの間にあります。
- ヘッドポインタとテールポインタの関係を利用して、キュー満杯判定(キュースペースが満杯かどうかの判定)と空判定(キューが空かどうかの判定)を実現し、この目的を達成するために、静的配列の容量をk+1 に設定する必要があります(つまり、もう 1 つの要素空間を設定します)。
- キューが空の状態: テール == ヘッド;
- キューがいっぱいの状態: (tail+1)%(k+1) == head; 別の状況:
- これから、最初にキューの完全で空のインターフェースを設計できます。
bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空 { assert(obj); return (obj->tail == obj->head); } bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满 { assert(obj); return ((obj->tail+1)%(obj->capacity +1) == obj->head); }
2. 配列循環キューのヒープ領域メモリ アプリケーション インターフェイス
- ヒープ上にMyCircularQueue 構造を作成し、同時にキューに (k+1)*sizeof(DataType) バイトの配列を適用します。
MyCircularQueue* myCircularQueueCreate(int k) //k个容量大小的循环队列的初始化接口 { MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue)); //开辟维护循环队列的结构体 if(NULL == tem) { perror("malloc failed"); exit(-1); } tem->arry = NULL; tem->capacity = k; //队列的数据容量为k tem->arry = (int*)malloc((k+1)*sizeof(int)); //开辟堆区数组 if(NULL == tem->arry) { perror("malloc failed"); exit(-1); } //将head,tail下标初始化为0 tem->head = 0; tem->tail = 0; return tem; }
3. データのデキューとエンキューのインタフェース実現
データのデキューとエンキューの図:
- 図に従って、データの入力と終了のインターフェイスを設計できます。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口 { assert(obj); if(myCircularQueueIsFull(obj)) { return false; } //确保队列没满 obj->arry[obj->tail]=value; obj->tail = (obj->tail + 1)%(obj->capacity +1); return true; }
bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return false; } //确保队列不为空 obj->head = (obj->head +1)%(obj->capacity +1); return true; }
4. その他の操作インターフェース
キュー ヘッド データを返すインターフェイス:
int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->arry[obj->head]; }
キューの最後にあるデータを返すインターフェイス:
int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity; //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置 return obj->arry[labelret]; }
キューのインターフェイスを破棄します。
void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口 { assert(obj); free(obj->arry); obj->arry = NULL; free(obj); obj = NULL; }
5. 配列循環キューの実装コードの概要
配列循環キューの全体的なコード:
typedef struct { int * arry; //指向堆区数组的指针 int head; //队头指针 int tail; //队尾指针(指向队尾数据的下一个位置)(不指向有效数据) int capacity;//静态队列容量 } MyCircularQueue; bool myCircularQueueIsEmpty(MyCircularQueue* obj); bool myCircularQueueIsFull(MyCircularQueue* obj); //顺序编译注意:先被使用而后被定义的函数要记得进行声明 MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口 { MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue)); //开辟维护循环队列的结构体 if(NULL == tem) { perror("malloc failed"); exit(-1); } tem->arry = NULL; tem->capacity = k; //队列的数据容量为k tem->arry = (int*)malloc((k+1)*sizeof(int)); //开辟堆区数组 if(NULL == tem->arry) { perror("malloc failed"); exit(-1); } tem->head = 0; tem->tail = 0; return tem; } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口 { assert(obj); if(myCircularQueueIsFull(obj)) { return false; } //确保队列没满 obj->arry[obj->tail]=value; obj->tail = (obj->tail + 1)%(obj->capacity +1); return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return false; } //确保队列不为空 obj->head = (obj->head +1)%(obj->capacity +1); return true; } int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->arry[obj->head]; } int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity; //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置 return obj->arry[labelret]; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空 { assert(obj); return (obj->tail == obj->head); } bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满 { assert(obj); return ((obj->tail+1)%(obj->capacity +1) == obj->head); } void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口 { assert(obj); free(obj->arry); obj->arry = NULL; free(obj); obj = NULL; }
Lituo 問題解決テスト:
3. 静的な一方向循環連結リストにより循環キューを実現
リンク リスト ノード構造の定義:
typedef struct listnode { int data; struct listnode * next; }ListNode;
リンク リスト循環キューの構造を維持します。
typedef struct { int capacity; //记录队列容量大小 ListNode * head; //指向队头节点 ListNode * tail; //指向队尾节点 } MyCircularQueue;
1. リンクリスト循環キューの構造設計
静的な一方向循環リンク リストの容量はk です。
- 配列循環キューと同様に、k+1 個のノードを持つ静的循環リンク リストも作成する必要があります。
- リンク リスト循環キューの全体構造の図:キューがいっぱいになる別の状況:
- リンクリストの巡回キューの満杯状態(キューの空きが満杯かどうかを判断する関係式):tail->next == head;
- リンクリスト循環キューの空判定条件(空キューか否かを判定する関係式):tail==head;
リンクリスト循環キューの満杯判定と空判定のインターフェース:
bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空 { assert(obj); return(obj->head == obj->tail); } bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满 { assert(obj); return (obj->tail->next == obj->head); }
2. 静的な一方向循環リンク リストのインターフェイスを作成する
インターフェイスを実装し、リンク リストの循環キューを維持する構造を作成し、k+1 の容量を持つ静的な一方向循環リンク リストを作成します。
MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口 { int NodeNum =k+1; //创建k+1个链表节点 MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue)); assert(object); //申请维护循环队列的结构体 object->capacity = k; ListNode * preNode = NULL; //用于记录前一个链接节点的地址 while(NodeNum) { if(NodeNum == k+1) { ListNode * tem = (ListNode *)malloc(sizeof(ListNode)); assert(tem); preNode = tem; object->tail = object->head=tem; //让tail和head指向同一个初始节点 } else { ListNode * tem = (ListNode *)malloc(sizeof(ListNode)); assert(tem); preNode->next = tem; //链接链表节点 preNode = tem; } NodeNum--; } preNode->next = object->head; //将表尾与表头相接 return object; }
3. データのデキューおよびエンキュー インターフェイス
データの入力と終了の図:
次の図に従って、データの入力と終了のインターフェイスを実現します。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)//数据入队接口(从队尾入队) { assert(obj); if(!obj || myCircularQueueIsFull(obj)) //确定队列没满 { return false; } obj->tail->data = value; //数据入队 obj->tail = obj->tail->next; return true; }
bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口 { assert(obj); if(!obj || myCircularQueueIsEmpty(obj)) { return false; } //数据出队 obj->head = obj->head->next; return true; }
4. その他のキュー操作インターフェース
キュー ヘッド データを返すインターフェイス:
int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->head->data; //返回队头元素 }
キューの最後にあるデータを返すインターフェイス:
int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } ListNode * tem = obj->head; while(tem->next != obj->tail) //寻找队尾元素 { tem=tem->next; } return tem->data; //返回队尾元素 }
キュー破棄インターフェース:
キュー破棄プロセスの図:
void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口 { assert(obj); //利用头指针来完成链表节点的释放 ListNode * endpoint = obj->head; //记录一个节点释放的终点 obj->head = obj->head->next; while(obj->head!=endpoint) { ListNode * tem = obj->head->next; free(obj->head); obj->head = tem; } free(endpoint); //释放掉终点节点 free(obj); //释放掉维护环形队列的结构体 }
5.静的連結リスト循環キュー全体コード
全体的なコード:
typedef struct listnode { int data; struct listnode * next; }ListNode; typedef struct { int capacity; ListNode * head; ListNode * tail; int taildata; //单向链表找尾复杂度为O(N),因此我们用一个变量来记录队尾数据 } MyCircularQueue; bool myCircularQueueIsEmpty(MyCircularQueue* obj); bool myCircularQueueIsFull(MyCircularQueue* obj); //顺序编译注意:先被使用而后被定义的函数要记得进行声明 MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口 { int NodeNum =k+1; //创建k+1个链表节点 MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue)); assert(object); //申请维护循环队列的结构体 object->capacity = k; ListNode * preNode = NULL; //用于记录前一个链接节点的地址 while(NodeNum) { if(NodeNum == k+1) { ListNode * tem = (ListNode *)malloc(sizeof(ListNode)); assert(tem); preNode = tem; object->tail = object->head=tem; //让tail和head指向同一个初始节点 } else { ListNode * tem = (ListNode *)malloc(sizeof(ListNode)); assert(tem); preNode->next = tem; //链接链表节点 preNode = tem; } NodeNum--; } preNode->next = object->head; //将表尾与表头相接 return object; } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口(从队尾入队) { assert(obj); if(!obj || myCircularQueueIsFull(obj)) //确定队列没满 { return false; } obj->tail->data = value; //数据入队 obj->tail = obj->tail->next; return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口 { assert(obj); if(!obj || myCircularQueueIsEmpty(obj)) { return false; } obj->head = obj->head->next; return true; } int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->head->data; } int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口 { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } ListNode * tem = obj->head; while(tem->next != obj->tail) //寻找队尾元素 { tem=tem->next; } return tem->data; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空 { assert(obj); return(obj->head == obj->tail); } bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满 { assert(obj); return (obj->tail->next == obj->head); } void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口 { assert(obj); //利用头指针来完成链表节点的释放 ListNode * endpoint = obj->head; //记录一个节点释放的终点 obj->head = obj->head->next; while(obj->head!=endpoint) { ListNode * tem = obj->head->next; free(obj->head); obj->head = tem; } free(endpoint); //释放掉终点节点 free(obj); //释放掉维护环形队列的结构体 }
leetcode ソリューション テスト: