記事内にはプロモーションが含まれています

【C言語】バブルソートをわかりやすく解説!昇順・降順・文字列の並び替え

【C言語】バブルソートをわかりやすく解説!昇順・降順・文字列の並び替え C言語

プログラミングにおけるアルゴリズム学習の第一歩として、必ずと言っていいほど登場するのが「バブルソート(基本交換法)」です。

「隣り合う数字を比較して入れ替える」というシンプルな仕組みですが、これをC言語で実装しようとすると、配列の操作やループ処理、さらにはポインタの概念など、重要な要素が凝縮されていることに気づきます。

しかし、いざコードを書こうとすると「ループの条件式がわからなくなる」「文字列のソートでコンパイルエラーが出る」といった壁にぶつかることも少なくありません。

この記事では、C言語におけるバブルソートの基本的な実装方法(昇順・降順)から、少し難易度の高い文字列配列のソート、そして関数化してポインタで配列を扱う方法まで、わかりやすく解説します。

【本記事の信頼性】
プロフィール
執筆者:マヒロ
  • 執筆者は元エンジニア
  • SES⇒大手の社内SE⇒独立
  • 現在はこじんまりとしたプログラミングスクールを運営
  • モットーは「利他の精神」

バブルソート(基本交換法)のアルゴリズムとは

バブルソートは、隣り合う要素同士を比較し、順序が逆であれば交換する(入れ替える)という操作を繰り返すことで、データを整列させるアルゴリズムです

整列されていく様子が、泡(Bubble)が水面に浮かんでいくように見えることからこの名前がつきました。

並び替えの仕組み

例えば [5, 3, 8, 1] という配列を昇順(小さい順)に並べる場合、以下のように動きます。

  1. 先頭の 5 と隣の 3 を比較。5 > 3 なので交換 → [3, 5, 8, 1]
  2. 次の 58 を比較。5 < 8 なのでそのまま。
  3. 次の 81 を比較。8 > 1 なので交換 → [3, 5, 1, 8]
  4. これで一番大きな 8 が末尾に確定しました。

この「走査(スキャン)」を、確定した部分を除いて繰り返すことで、最終的にすべての要素が整列されます。

【基本】バブルソートで数値を「昇順」に並び替える

まずは最も基本的な、整数の配列を小さい順(昇順)に並び替えるプログラムを見てみましょう。

二重ループの構造を理解することがポイントです。

#include <stdio.h>

#define SIZE 5

int main(void) {
    int array[SIZE] = {64, 34, 25, 12, 22};
    int i, j, temp;

    printf("ソート前: ");
    for (i = 0; i < SIZE; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");

    // バブルソートの実装
    // 外側のループ:確定させる要素の個数(0からSIZE-2まで)
    for (i = 0; i < SIZE - 1; i++) {
        // 内側のループ:隣同士を比較して交換していく
        // 末尾からi個はすでに確定しているので、(SIZE - 1 - i) まで比較
        for (j = 0; j < SIZE - 1 - i; j++) {
            // 前の要素の方が大きければ交換(昇順)
            if (array[j] > array[j + 1]) {
                temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }

    printf("ソート後: ");
    for (i = 0; i < SIZE; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");

    return 0;
}

実行結果

ソート前: 64 34 25 12 22 
ソート後: 12 22 25 34 64 

このプログラムの核となるのは、if (array[j] > array[j + 1]) の部分です。

ここで「左の要素」が「右の要素」より大きい場合に、一時変数 temp を使って値を入れ替えています(スワップ)。

内側のループ条件 j < SIZE - 1 - i は、ループが進むごとに末尾の確定した要素を比較対象から除外するための工夫です。
これにより無駄な比較を減らしています。

条件を変えて「降順」にソートする

大きい順(降順)に並び替えたい場合は、比較の条件式を一つ変えるだけで実現できます。
ロジック自体は昇順と全く同じです。

降順の実装コード

// (前略)
        for (j = 0; j < SIZE - 1 - i; j++) {
            // 前の要素の方が小さければ交換(降順)
            if (array[j] < array[j + 1]) { // ここを '<' に変更
                temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
// (後略)

変更点は if 文の不等号を > から < に変えただけです。

「左の要素が右より小さい場合」に交換を行うことで、大きい値がどんどん左(先頭)に、小さい値が右(末尾)に移動していきます。

【応用】ポインタと関数を使ったバブルソート

実務的なプログラムでは、ソート処理を main 関数に直書きせず、独立した関数として切り出すのが一般的です。

配列を関数に渡す際は、ポインタの知識が必要になります。

関数化したバブルソートの実装例

#include <stdio.h>

// 値を交換する関数
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// バブルソートを行う関数
// 配列はポインタとして受け取る
void bubbleSort(int *arr, int size) {
    int i, j;
    for (i = 0; i < size - 1; i++) {
        for (j = 0; j < size - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                // アドレスを渡して交換する
                swap(&arr[j], &arr[j + 1]);
            }
        }
    }
}

int main(void) {
    int data[] = {5, 2, 9, 1, 5, 6};
    int n = sizeof(data) / sizeof(data[0]); // 要素数を計算

    bubbleSort(data, n);

    printf("ソート結果: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");

    return 0;
}

実行結果

ソート結果: 1 2 5 5 6 9 

swap 関数では、変数のアドレス(ポインタ)を受け取り、その中身を直接書き換えています。

bubbleSort 関数では、配列の先頭アドレスを int *arr で受け取り、arr[j] のように添字アクセスしています(C言語では *(arr + j)arr[j] は同義です)。

このように関数化することで、どんな配列でも汎用的にソートできるようになります。

文字列(char配列)をバブルソートする方法

C言語で最もつまづきやすいのが「文字列のソート」です。

文字列は単純な比較演算子(><)では比較できず、代入(=)もできません。

標準ライブラリの strcmpstrcpy を使う必要があります。

文字列配列の並び替えコード

#include <stdio.h>
#include <string.h> // 文字列操作用

#define NUM 4
#define LEN 20

int main(void) {
    // 2次元配列として文字列リストを定義
    char names[NUM][LEN] = {"Sato", "Tanaka", "Suzuki", "Aikawa"};
    char temp[LEN];
    int i, j;

    for (i = 0; i < NUM - 1; i++) {
        for (j = 0; j < NUM - 1 - i; j++) {
            // 文字列の比較には strcmp を使用
            // strcmp(A, B) > 0 ならば、Aの方が辞書順で後(大きい)
            if (strcmp(names[j], names[j + 1]) > 0) {
                // 文字列の入れ替えには strcpy を使用
                strcpy(temp, names[j]);
                strcpy(names[j], names[j + 1]);
                strcpy(names[j + 1], temp);
            }
        }
    }

    printf("辞書順ソート後:\n");
    for (i = 0; i < NUM; i++) {
        printf("%s\n", names[i]);
    }

    return 0;
}

実行結果

辞書順ソート後:
Aikawa
Sato
Suzuki
Tanaka

文字列の比較には strcmp 関数を使います。
strcmp(str1, str2) は、str1 が str2 より辞書順で後ろにある場合に正の値を返します。

また、文字列の交換には temp = names[j]; のような代入ができないため、strcpy 関数を使って文字データをメモリ上でコピーしています。

バブルソートの計算量とメリット・デメリット

最後に、アルゴリズムとしてのバブルソートの性能について整理しておきましょう。

実務で大量のデータを扱う際には、クイックソートなどのより高速なアルゴリズムが選ばれることが多いです。

計算量は $O(n^2)$

バブルソートは二重ループを使うため、データ数 $n$ に対して計算量が $n^2$ に比例して増えていきます。

データが10倍になると、計算時間は約100倍になります。そのため、数万件以上のデータを扱うには不向きです。

メリットとデメリット

【メリット】

  • アルゴリズムが単純で理解しやすい。
  • コードの実装が簡単でバグが出にくい。
  • 安定ソートである(同じ値の要素の順番が変わらない)。

【デメリット】

  • 計算速度が遅い(クイックソートは $O(n \log n)$ )。
  • 交換回数が多いため、書き込み処理が多い環境では不利。

C言語のスキルを活かして年収を上げる方法

以上、C言語のバブルソートについて解説してきました。

なお、C言語のスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。

C言語を扱えるエンジニアは比較的希少価値が高く、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。

なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。

今すぐ転職する気がなくとも、とりあえず転職エージェントに無料登録しておくだけで、スカウトが届いたり、思わぬ好待遇の求人情報が送られてきたりするというメリットがあります。

併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなC言語の案件を探しやすくなります。

転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。