データ構造: 循環キューの実装 (leetcode622. 循環キューの設計)

 

目次

1. 循環キューの簡単な紹介

2.静的配列で循環キューを実装する

1. アレイ循環キュー構造の設計

2. 配列循環キューのヒープ領域メモリ アプリケーション インターフェイス 

3. データのデキューとエンキューのインタフェース実現

4. その他の操作インターフェース

5. 配列循環キューの実装コードの概要 

3. 静的な一方向循環連結リストにより循環キューを実現 

1. リンクリスト循環キューの構造設計

2. 静的な一方向循環リンク リストのインターフェイスを作成する

3. データのデキューおよびエンキュー インターフェイス

4. その他のキュー操作インターフェース

5.静的連結リスト循環キュー全体コード


問題のソース: 622. 円形キューの設計 - Leetcode

1. 循環キューの簡単な紹介

  • 循環キューは一般に、データが先入れ先出しの原則に準拠する静的線形データ構造です
  • 循環キューのコンテナーの先頭アドレスコンテナーの末尾アドレスは、特定の操作 (ポインターのリンク、配列の添字など) によって接続されるため、コンテナー空間の再利用が実現されます(非循環静的キューでは、キューがいっぱいになると、キューの先頭にまだスペースがあるにもかかわらず、次の要素を挿入することはできません)

 

2.静的配列で循環キューを実装する

キューの構造を維持します。

typedef struct 
{
    int * arry;  //指向堆区数组的指针
    int head;    //队头指针
    int tail;    //队尾指针(指向队尾数据的下一个位置)(不指向有效数据)
    int capacity;//静态队列的容量
} MyCircularQueue;

1. アレイ循環キュー構造の設計

静的配列の容量が k ( k 個のデータを格納できる)であると仮定します

  • キューの基本的なデータ構造によると、配列内の有効なデータ空間を維持するために使用される2 つのポインター、つまりヘッド ポインターとテール ポインターがあり、ヘッド ポインターはキューの先頭にあるデータを指すために使用されます。 、および末尾は、キューの末尾にあるデータの次の位置を指すために使用されます(つまり、末尾ポインタは有効なデータを指しません)。
  •  図に示すように、有効なデータのメモリ空間は、ヘッド ポインタとテール ポインタの間にあります。
  • ヘッドポインタとテールポインタの関係を利用して、キュー満杯判定(キュースペースが満杯かどうかの判定)と空判定(キューが空かどうかの判定)を実現し、この目的を達成するために、静的配列の容量をk+1 に設定する必要があります(つまり、もう 1 つの要素空間を設定します)。 
  1. キューが空の状態: テール == ヘッド;
  2. キューがいっぱいの状態: (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 個のノードを持つ静的循環リンク リストも作成する必要があります。
  • リンク リスト循環キューの全体構造の図:キューがいっぱいになる別の状況:
  1.  リンクリストの巡回キューの満杯状態(キューの空きが満杯かどうかを判断する関係式):tail->next == head;
  2.  リンクリスト循環キューの空判定条件(空キューか否かを判定する関係式):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 ソリューション テスト:

 

 

 

おすすめ

転載: blog.csdn.net/weixin_73470348/article/details/129167702