Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

C言語で連結リストを使ったスタックの実装方法を徹底解説

はじめに

スタック(Stack)は「後入れ先出し(LIFO:Last In First Out)」という特徴を持つ基本的なデータ構造です。配列ではなく連結リスト(リンクリスト)を使ってスタックを実装すると、メモリを動的に確保できるため、スタックオーバーフロースタックアンダーフローといった問題を効果的に回避できます。

C言語におけるスタックに対する主な操作は以下の2つです。

  • Push(プッシュ) … スタックの先頭に要素を追加する
  • Pop(ポップ) … スタックの先頭から要素を取り出す

この記事では、それぞれの操作の仕組みと、実際に動作するC言語のサンプルプログラムを詳しく解説します。


Push(プッシュ)の実装

Pushは、新しいノードを動的に確保し、スタックの先頭に挿入する処理です。連結リストを使った基本的な実装は次のとおりです。

&item = 10;
newnode = (node*) malloc (sizeof (node));
newnode->data = item;
newnode->link = NULL;
newnode->link = start;
start = newnode;

この処理では、まず malloc によって新しいノードのメモリを確保し、データを格納したうえで、既存の先頭ノードへのポインタをつなぎ替えます。最後に start を新しいノードに更新することで、要素がスタックの先頭に追加されます。

Pop(ポップ)の実装

Popは、スタックの先頭にある要素を削除して取り出す処理です。スタックが空の場合に削除を行おうとするとエラーになるため、必ず空チェックを行います。基本構文は以下のとおりです。

構文

if (start == NULL)
    printf("Deletion is not possible.List is empty");
else {
    temp = start;
    start = start->link;
    free(temp);
}

このコードでは、先頭ノードが存在しない場合は「削除不可・リストが空」というメッセージを表示し、存在する場合は一時ポインタ temp に先頭ノードを退避させてから、free 関数でメモリを解放します。これによりメモリリークを防げます。

サンプルプログラム全体

以下は、連結リストを使用してスタックを実装した完全なC言語プログラムです。Push、Pop、Top(先頭要素の参照)、Empty判定、表示、要素数カウント、スタック破棄など、多彩な機能をメニュー形式で提供しています。

#include <stdio.h>
#include <stdlib.h>

struct node {
    int info;
    struct node *ptr;
} *top, *top1, *temp;

int topelement();
void push(int data);
void pop();
void empty();
void display();
void destroy();
void stack_count();
void create();

int count = 0;

void main() {
    int no, ch, e;
    printf("\n 1 - Push");
    printf("\n 2 - Pop");
    printf("\n 3 - Top");
    printf("\n 4 - Empty");
    printf("\n 5 - Exit");
    printf("\n 6 - Display");
    printf("\n 7 - Stack Count");
    printf("\n 8 - Destroy stack");
    create();
    while (1) {
        printf("\n Enter choice : ");
        scanf("%d", &ch);
        switch (ch) {
            case 1:
                printf("Enter element : ");
                scanf("%d", &no);
                push(no);
                break;
            case 2:
                pop();
                break;
            case 3:
                if (top == NULL)
                    printf("stack is empty");
                else {
                    e = topelement();
                    printf("\n Top element : %d", e);
                }
                break;
            case 4:
                empty();
                break;
            case 5:
                exit(0);
            case 6:
                display();
                break;
            case 7:
                stack_count();
                break;
            case 8:
                destroy();
                break;
            default:
                printf(" wrong choice:Try again ");
                break;
        }
    }
}

// 空のスタックを作成
void create() {
    top = NULL;
}

void stack_count() {
    printf("\n no: of elements in stack : %d", count);
}

// データをプッシュ
void push(int data) {
    if (top == NULL) {
        top = (struct node *)malloc(1 * sizeof(struct node));
        top->ptr = NULL;
        top->info = data;
    } else {
        temp = (struct node *)malloc(1 * sizeof(struct node));
        temp->ptr = top;
        temp->info = data;
        top = temp;
    }
    count++;
}

void display() {
    top1 = top;
    if (top1 == NULL) {
        printf("empty stack");
        return;
    }
    while (top1 != NULL) {
        printf("%d ", top1->info);
        top1 = top1->ptr;
    }
}

void pop() {
    top1 = top;
    if (top1 == NULL) {
        printf("\n error");
        return;
    } else
        top1 = top1->ptr;
        printf("\n Popped value : %d", top->info);
        free(top);
        top = top1;
        count--;
}

int topelement() {
    return (top->info);
}

// スタックが空かどうか確認
void empty() {
    if (top == NULL)
        printf("\n empty stack");
    else
        printf("\n stack not empty with %d values", count);
}

void destroy() {
    top1 = top;
    while (top1 != NULL) {
        top1 = top->ptr;
        free(top);
        top = top1;
        top1 = top1->ptr;
    }
    free(top1);
    top = NULL;
    printf("\n all are destroyed");
    count = 0;
}

実行結果

上記のプログラムをコンパイルして実行すると、以下のような出力が得られます。各操作が正しく動作している様子を確認できます。

1 - Push
2 - Pop
3 - Top
4 - Empty
5 - Exit
6 - Display
7 - Stack Count
8 - Destroy stack
Enter choice: 1
Enter element: 23
Enter choice: 1
Enter element: 45
Enter choice: 1
Enter element: 56
Enter choice: 2
Popped value: 56
Enter choice: 6
45 23
Enter choice: 8
all are destroyed
Enter choice: 6
empty stack
Enter choice: 5

まとめ

このように、C言語では連結リストを利用することで、サイズに制限のない柔軟なスタックを実装できます。動的メモリ確保(malloc / free)を適切に行うことで、オーバーフローやアンダーフローの問題を回避しながら、PushやPopなどの基本操作を安全に実現できる点が大きなメリットです。ぜひ実際にコードを動かして、スタックの動作原理を体感してみてください。

  1. C言語で連結リストを使った優先度付きキューの実装方法

    本記事では、整数値の「データ」と「優先度」が与えられたとき、指定された優先度に従って連結リスト(リンクリスト)を構築し、結果を表示する方法を解説します。 優先度付きキューとは キューはFIFO(First In, First Out:先入れ先出し)方式のデータ構造であり、最初に挿入された要素が最初に取り除かれます。 一方、優先度付きキュー(プライオリティキュー)は、要素の挿入・削除を「優先度」に基づいて行えるキューの一種です。キュー、スタック、連結リストなどのデータ構造を用いて実装でき、以下のルールに従って動作します。 優先度が最も高いデータ(要素)は、優先度が低いものよりも先に処理される。

  2. C言語で連結リストの交互ノードを出力する方法(反復法)

    この問題では、与えられた連結リストから交互のノードを出力するプログラムを作成します。つまり、1つ飛ばしでノードを表示していく処理を、反復法(イテレーティブな手法)を用いて実装します。 反復法とは、条件が真(true)である限り繰り返し実行されるループを使用する手法のことです。 例えば、リストに 29、34、43、56、88 というノードが格納されている場合、出力結果は交互ノードである 29、43、88 となります。 例 入力: 29->34->43->56->88 出力: 29 43 88 アプローチ 基本的な考え方は、リストを最後のノードまで走査するというものです。走