2つのキューを使用したスタックの実装

2つのキューを使用したスタックの実装

問題分析

この問題は、配列やリンクリストでスタックを実装するのではなく、キューを使用してスタックを実装することを求めています。キューとスタックの関係は逆で、スタックは後入れ先出し(LIFO)の特性を持つのに対し、キューは先入れ先出し(FIFO)の特性を持っています。したがって、この問題はキューの性質をスタックの性質に変換し、キューを使用してスタックを実装する能力を評価しています。

2つのキューを使用してスタックのプッシュ、ポップ、トップ要素の取得を実装する方法

(キューの先入れ先出しの特性をスタックの後入れ先出しの特性に変換する方法)

スタックのプッシュ操作

2つのキューを使用してスタックを実装する場合、スタックのプッシュ操作は、2つのキューの中で空でないキューに対して行われます(注:最初に両方のキューが空の場合は、どちらか一方を選んでプッシュ操作を行います)。スタックのプッシュ操作は、空でないキューに対するエンキュー操作です。

2つのキューを使用してスタックの後入れ先出しのポップ操作をシミュレートする方法

2つのキューの中で空でないキューにn個のデータがあるとします。スタックを2つのキューで実装する場合、常に一方のキューにデータが存在し(空でないキュー)、もう一方のキューは空であるように保たれているため、スタックのポップ操作の考え方は、空でないキューのn-1個のデータを空のキューに転送し、元の空でないキューに残った最後のキュー先頭データをスタックトップ要素としてポップすることです。

注意:①データ転送前に、2つのキューのうち一方は空でないキュー、もう一方は空のキューです。空でないキューのn-1個のデータを空のキューに転送する過程では、スタックの2つのキューが両方とも空でないキューになる可能性がありますが、最後に元の空でないキューに残った最後のキュー先頭データをスタックトップ要素としてポップすることで、2つのキューは再び一方が空で、もう一方が空でないキューの状態に戻ります。②全体として、スタックのポップ操作が完了すると、スタック内の2つのキューは、一方が空で、もう一方が空でないキューの状態を維持します。

2つのキューの中の空でないキューのキュー末尾データを取得してスタックのトップ要素をシミュレートする方法

2つのキューは常に一方がデータを保持し(空でないキュー)、もう一方が空のキューであるように保たれているため、スタックの要素は常に空でないキューに保存され、かつ空でないキューのキュー末尾データがスタックのトップデータであるため、空でないキューのキュー末尾データを取得することでスタックのトップ要素を取得する操作を実現できます。

コード分析

初期化関数myStackCreate

スタックの構造体は、キューq1とq2をメンバーとして含むだけで構成されます。これは、スタックが2つのキューで実装されるためです。

スタックのプッシュ関数myStackPush

スタックのプッシュ操作は、2つのキューの中で空でないキューに対して行われます。両方のキューが空の場合は、どちらか一方を選んでプッシュ操作を行います。スタックのプッシュ操作は、空でないキューに対するエンキュー操作に相当します。

スタックのポップ関数myStackPop

空でないキューにn個のデータがあるとします。スタックを2つのキューで実装する場合、常に一方のキューにデータが存在し、もう一方のキューは空であるように保たれているため、スタックのポップ操作の考え方は、空でないキューのn-1個のデータを空のキューに転送し、元の空でないキューに残った最後のキュー先頭データをスタックトップ要素としてポップすることです。

スタックトップ要素取得関数myStackTop

2つのキューは常に一方がデータを保持し(空でないキュー)、もう一方が空のキューであるように保たれているため、スタックの要素は常に空でないキューに保存され、かつ空でないキューのキュー末尾データがスタックのトップデータであるため、空でないキューのキュー末尾データを取得することでスタックのトップ要素を取得する操作を実現できます。

スタックが空かどうかを判断する関数myStackEmpty

スタックの2つのキューが両方とも空の場合、そのスタックは空のスタックであると判断できます。

スタックの解放関数myStackFree

スタックの動的メモリ、キューq1のリンクリストの動的メモリ、キューq2のリンクリストの動的メモリをすべて解放する必要があります。

コード実装

// キューのデータ型
typedef int QDataType;

// キューのノード構造体
typedef struct QueueNode {
    struct QueueNode* next;
    QDataType data;
} QNode;

// キューの構造体
typedef struct Queue {
    QNode* front;  // キューの先頭
    QNode* rear;   // キューの末尾
    int count;     // キュー内の要素数
} Queue;

// スタックの構造体
typedef struct {
    Queue queue1;
    Queue queue2;
} Stack;

// キューの初期化
void queue_init(Queue* q) {
    q->front = q->rear = NULL;
    q->count = 0;
}

// キューの解放
void queue_destroy(Queue* q) {
    QNode* current = q->front;
    while (current != NULL) {
        QNode* temp = current;
        current = current->next;
        free(temp);
    }
    q->front = q->rear = NULL;
    q->count = 0;
}

// キューに要素を追加(エンキュー)
void queue_enqueue(Queue* q, QDataType value) {
    QNode* new_node = (QNode*)malloc(sizeof(QNode));
    if (new_node == NULL) {
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }
    new_node->data = value;
    new_node->next = NULL;
    
    if (q->rear == NULL) {
        q->front = q->rear = new_node;
    } else {
        q->rear->next = new_node;
        q->rear = new_node;
    }
    q->count++;
}

// キューから要素を削除(デキュー)
QDataType queue_dequeue(Queue* q) {
    if (q->front == NULL) {
        fprintf(stderr, "Queue is empty\n");
        exit(EXIT_FAILURE);
    }
    
    QNode* temp = q->front;
    QDataType value = temp->data;
    q->front = q->front->next;
    
    if (q->front == NULL) {
        q->rear = NULL;
    }
    
    free(temp);
    q->count--;
    return value;
}

// スタックの初期化
Stack* stack_create() {
    Stack* s = (Stack*)malloc(sizeof(Stack));
    queue_init(&s->queue1);
    queue_init(&s->queue2);
    return s;
}

// スタックに要素を追加(プッシュ)
void stack_push(Stack* s, QDataType value) {
    // データを空でないキューに追加
    if (s->queue1.count > 0) {
        queue_enqueue(&s->queue1, value);
    } else {
        queue_enqueue(&s->queue2, value);
    }
}

// スタックから要素を削除(ポップ)
QDataType stack_pop(Stack* s) {
    Queue* source = NULL;
    Queue* destination = NULL;
    
    // データが存在するキューを特定
    if (s->queue1.count > 0) {
        source = &s->queue1;
        destination = &s->queue2;
    } else {
        source = &s->queue2;
        destination = &s->queue1;
    }
    
    // ソースキューから destinationキューへデータを転送(最後の要素を除く)
    while (source->count > 1) {
        queue_enqueue(destination, queue_dequeue(source));
    }
    
    // 最後の要素をポップして返す
    return queue_dequeue(source);
}

// スタックのトップ要素を取得
QDataType stack_top(Stack* s) {
    Queue* non_empty_queue = NULL;
    
    if (s->queue1.count > 0) {
        non_empty_queue = &s->queue1;
    } else {
        non_empty_queue = &s->queue2;
    }
    
    // キューの末尾要素を返す(スタックのトップ)
    QNode* current = non_empty_queue->front;
    QDataType top_value;
    while (current != NULL) {
        top_value = current->data;
        current = current->next;
    }
    return top_value;
}

// スタックが空かどうかを確認
bool stack_is_empty(Stack* s) {
    return s->queue1.count == 0 && s->queue2.count == 0;
}

// スタックの解放
void stack_destroy(Stack* s) {
    queue_destroy(&s->queue1);
    queue_destroy(&s->queue2);
    free(s);
}

テストコード

#include 
#include 
#include 

// 上記のスタックとキューの実装を含める

int main() {
    Stack* stack = stack_create();
    
    // スタックに要素をプッシュ
    stack_push(stack, 10);
    stack_push(stack, 20);
    stack_push(stack, 30);
    
    printf("スタックトップ: %d\n", stack_top(stack));  // 出力: 30
    
    // スタックから要素をポップ
    printf("ポップされた要素: %d\n", stack_pop(stack));  // 出力: 30
    printf("新しいスタックトップ: %d\n", stack_top(stack));  // 出力: 20
    
    // スタックが空かどうかを確認
    printf("スタックは空ですか? %s\n", stack_is_empty(stack) ? "はい" : "いいえ");  // 出力: いいえ
    
    // スタックを解放
    stack_destroy(stack);
    
    return 0;
}

タグ: スタック キュー データ構造 C言語 アルゴリズム

7月24日 20:46 投稿