[C++] いくつかの一般的な並べ替えアルゴリズムを整理しました

ヒント: 1. バブル ソート 2. 選択ソート 3. 挿入ソート 4. ヒル ソート 5. クイック ソート 6. マージ ソート 7. ヒープ ソート 8. カウンティング ソート 9. バケット ソート 10. 基数ソート

目次

1. バブルソート

2. 選択ソート       

3. 挿入ソート

4. ヒルソート

5. クイックソート

6. マージソート

7. ヒープソート      

8. カウントソート

9. バケットソート

10. 基数ソート


1. バブルソート

        バブル ソートは、配列全体がソートされるまで、隣接する 2 つの要素を繰り返し比較し、それらの位置を交換する単純なソート アルゴリズムです。バブル ソートの基本的な考え方は、水に浮かぶ泡のように、より大きな要素を継続的に後方に移動させることであるため、バブル ソートという名前が付けられました。

バブルソートの具体的な手順は次のとおりです。

  1. 配列の最初の要素から開始して、隣接する 2 つの要素が比較されます。
  2. 前の要素が後の要素より大きい場合、2 つの要素の位置を交換します。
  3. 隣接する要素の比較と交換は、最後の要素まで継続されます。
  4. 上記の手順を繰り返し、各反復で最大の要素を配列の末尾に移動します。
  5. すべての要素が並べ替えられるまで、次の繰り返しを続けます。

        バブル ソートの時間計算量は O(n^2) です。ここで、n は配列の長さです。バブル ソートは最も効率的な並べ替えアルゴリズムではありませんが、理解するのが簡単で、状況によっては適用できます。

//冒泡排序
void bubbleSort(int array[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (array[j] > array[j + 1]) {
                // 交换相邻元素的位置
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

2. 選択ソート       

        選択ソートは、毎回未ソート部分から最小 (または最大) の要素を選択し、ソート済み部分の最後に配置する単純なソート アルゴリズムでもあります。選択ソートの基本的な考え方は、未ソート部分の最小要素を見つけて、それをソート済み部分の最後に交換し、順序付けられたシーケンスを徐々に構築することです。

選択ソートの具体的な手順は次のとおりです。

  1. それが最小の要素であると仮定して、配列の最初の要素から始めます。
  2. ソートされていない部分で、トラバースして最小の要素を見つけ、そのインデックスを記録します。
  3. 見つかった最小の要素を、ソートされていないセクションの最初の要素と交換します。
  4. 上記のステップが繰り返され、各反復では、ソートされていない部分の最小要素がソートされた部分の最後に配置されます。
  5. すべての要素が並べ替えられるまで、次の繰り返しを続けます。

        選択ソートの時間計算量は O(n^2) です。ここで、n は配列の長さです。バブル ソートと同様に、選択ソートも単純ですが効率の悪い並べ替えアルゴリズムです。

//选择排序
void selectionSort(int array[], int size) {
    for (int i = 0; i < size - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < size; j++) {
            if (array[j] < array[minIndex]) {
                minIndex = j;
            }
        }
        // 交换最小元素和当前位置的元素
        int temp = array[i];
        array[i] = array[minIndex];
        array[minIndex] = temp;
    }
}

3. 挿入ソート

        挿入ソートは、配列をソート済みの部分とソートされていない部分に分割し、ソートされていない要素をソート済みの部分の正しい位置に 1 つずつ挿入する、シンプルで直感的なソート アルゴリズムです。挿入ソートの基本的な考え方は、ソートされた部分の正しい位置に要素を挿入し、ソートされた部分の長さを継続的に拡張することです。

挿入ソートの具体的な手順は次のとおりです。

  1. 配列の 2 番目の要素から開始します。これが挿入される要素とみなされます。
  2. 挿入する要素とソートされた部分の要素を比較して、適切な位置を見つけます。
  3. ソートされたセクション内の、挿入される要素より大きい要素を移動して、要素を挿入するためのスペースを確保します。
  4. ソートされた部分の正しい位置に挿入する要素を挿入します。
  5. 次の反復に進み、次の未ソート要素をソート済み部分に挿入します。

        挿入ソートの時間計算量は O(n^2) です。ここで、n は配列の長さです。挿入ソートは、最悪の場合、他のソート アルゴリズムほどのパフォーマンスは得られませんが、場合によっては、より優れたパフォーマンスを発揮する可能性があります。

//插入排序
void insertionSort(int array[], int size) {
    for (int i = 1; i < size; i++) {
        int key = array[i];
        int j = i - 1;

        while (j >= 0 && array[j] > key) {
            array[j + 1] = array[j];
            j--;
        }

        array[j + 1] = key;
    }
}

4. ヒルソート

        ヒル ソートは効率的な挿入ソート アルゴリズムの改良版であり、配列をいくつかのサブシーケンスに分割してソートし、サブシーケンスの長さを徐々に減らし、最終的に配列全体のソートを完了します。ヒル ソートの特徴は、最初に複数の挿入ソートを実行できるため、小さな要素を適切な位置にすばやく移動できることです。

ヒルソートの具体的な手順は次のとおりです。

  1. インクリメントシーケンス (通常は配列の長さの半分) を選択し、インクリメントシーケンスに従って配列をサブシーケンスに分割します。
  2. 各サブシーケンスに対して挿入ソートを実行します。つまり、各サブシーケンス内の要素に対して直接挿入ソートを実行します。
  3. 増分シーケンスを徐々に縮小し、増分が 1 になるまでステップ 2 を繰り返します。
  4. 最後に、増分 1 の挿入ソートが実行され、配列全体のソートが完了します。

        ヒル ソートの時間計算量は、インクリメント シーケンスの選択によって異なりますが、一般的には O(n^2) です。それにもかかわらず、実際には、ヒル ソートは一般に単純な挿入ソートよりも優れたパフォーマンスを発揮します。

//希尔排序
void shellSort(int array[], int size) {
    for (int gap = size / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < size; i++) {
            int temp = array[i];
            int j;

            for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
                array[j] = array[j - gap];
            }

            array[j] = temp;
        }
    }
}

5. クイックソート

        クイックソートは、分割統治の考え方を使用した効率的なソートアルゴリズムです。これは、ピボット要素を選択することによって配列を 2 つのサブ配列に分割します。そのうちの 1 つはすべての要素がピボット要素より小さいものであり、もう 1 つはすべての要素がピボット要素より大きいものであり、その後 2 つのサブ配列を再帰的に並べ替えます。

クイックソートの具体的な手順は次のとおりです。

  1. 参照要素を選択します。配列内の任意の要素を選択できます。
  2. 配列を 2 つのサブ配列に分割します。1 つはピボット要素よりも小さい要素を含み、もう 1 つはピボット要素よりも大きい要素を含みます。
  3. 2 つの部分配列に対してクイック ソートを再帰的に呼び出し、部分配列の長さが 1 または 0 になったときに再帰を停止します。
  4. サブ配列をマージします。つまり、ピボット要素と 2 つのソートされたサブ配列をマージします。

        クイックソートの時間計算量は O(nlogn) です。ここで、n は配列の長さです。これは一般に、実際には最も高速な並べ替えアルゴリズムの 1 つですが、最悪の場合にはパフォーマンスが低下する可能性があります。

//快速排序
int partition(int array[], int low, int high) {
    int pivot = array[high];
    int i = low - 1;

    for (int j = low; j < high; j++) {
        if (array[j] < pivot) {
            i++;
            // 交换元素
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    // 将基准元素放置在正确的位置
    int temp = array[i + 1];
    array[i + 1] = array[high];
    array[high] = temp;

    return i + 1;
}

void quickSort(int array[], int low, int high) {
    if (low < high) {
        int pivot = partition(array, low, high);
        quickSort(array, low, pivot - 1);
        quickSort(array, pivot + 1, high);
    }
}

6. マージソート

        マージ ソートは、分割統治の考え方を使用した効率的な並べ替えアルゴリズムです。配列を小さなサブ配列に分割し、次にこれらのサブ配列をソートし、最後にそれらをソートされた配列にマージします。マージ ソートの鍵となるのは、2 つの順序付けされた部分配列を結合して順序付けされた配列を取得するマージ操作です。

マージソートの具体的な手順は次のとおりです。

  1. サブ配列の長さが 1 になるまで、配列をより小さなサブ配列に分割します。
  2. 各部分配列を並べ替えるには、マージソートの再帰呼び出しを使用できます。
  3. 2 つのソートされたサブ配列をマージして、より大きなソートされた配列を取得します。
  4. マージ操作は、すべてのサブ配列が完全にソートされた配列にマージされるまで繰り返されます。

        マージ ソートの時間計算量は O(nlogn) です。ここで、n は配列の長さです。これは安定した並べ替えアルゴリズムであり、大規模なデータを処理する場合に優れたパフォーマンスを発揮します。

//归并排序
void merge(int array[], int low, int mid, int high) {
    int n1 = mid - low + 1;
    int n2 = high - mid;

    int leftArray[n1];
    int rightArray[n2];

    for (int i = 0; i < n1; i++) {
        leftArray[i] = array[low + i];
    }

    for (int j = 0; j < n2; j++) {
        rightArray[j] = array[mid + 1 + j];
    }

    int i = 0;
    int j = 0;
    int k = low;

    while (i < n1 && j < n2) {
        if (leftArray[i] <= rightArray[j]) {
            array[k] = leftArray[i];
            i++;
        } else {
            array[k] = rightArray[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        array[k] = leftArray[i];
        i++;
        k++;
    }

    while (j < n2) {
        array[k] = rightArray[j];
        j++;
        k++;
    }
}

void mergeSort(int array[], int low, int high) {
    if (low < high) {
        int mid = low + (high - low) / 2;
        mergeSort(array, low, mid);
        mergeSort(array, mid + 1, high);
        merge(array, low, mid, high);
    }
}

7. ヒープソート      

         ヒープソートは、ソートにヒープのデータ構造を利用し、配列を完全な二分木とみなし、最大ヒープまたは最小ヒープを構築することでソートを実現します。ヒープでは、最大ヒープによって各ノードの値がその子ノードの値以上であることが保証され、最小ヒープによって各ノードの値がその子ノードの値以下であることが保証されます。 。

ヒープソートの具体的な手順は次のとおりです。

  1. 配列を最大ヒープまたは最小ヒープに構築します。
  2. ヒープの最上位要素 (最大または最小の要素) をヒープ内の最後の要素と交換します。
  3. ヒープのサイズを 1 減らして、ヒープのプロパティを満たすようにヒープのサイズを変更します。
  4. ヒープのサイズが 1 になり、ソートが完了するまで、手順 2 と 3 を繰り返します。

        ヒープ ソートの時間計算量は O(nlogn) です。ここで、n は配列の長さです。不安定な並べ替えアルゴリズムですが、パフォーマンスが良く、実用的なアプリケーションで広く使用されています。

//堆排序
void heapify(int array[], int size, int root) {
    int largest = root;
    int left = 2 * root + 1;
    int right = 2 * root + 2;

    if (left < size && array[left] > array[largest]) {
        largest = left;
    }

    if (right < size && array[right] > array[largest]) {
        largest = right;
    }

    if (largest != root) {
        // 交换元素
        int temp = array[root];
        array[root] = array[largest];
        array[largest] = temp;

        heapify(array, size, largest);
    }
}

void heapSort(int array[], int size) {
    for (int i = size / 2 - 1; i >= 0; i--) {
        heapify(array, size, i);
    }

    for (int i = size - 1; i > 0; i--) {
        // 交换堆顶元素和当前位置元素
        int temp = array[0];
        array[0] = array[i];
        array[i] = temp;

        heapify(array, i, 0);
    }
}

8. カウントソート

        カウンティング ソートは非比較ソート アルゴリズムであり、特定の範囲内の整数をソートするのに適しています。その基本的な考え方は、各要素の出現数を数え、要素のサイズの順序に従って配列を並べ替えることです。

ソートをカウントする具体的な手順は次のとおりです。

  1. ソートする配列の最大値を見つけて、count 配列のサイズを決定します。
  2. 配列内の各要素の出現数をカウントし、それを count 配列に格納します。
  3. count 配列内の各要素の累積値を計算して、並べ替えられた配列内の各要素の位置を取得します。
  4. 一時配列を作成し、ソートする配列内の要素を count 配列内の位置に従って順番に一時配列に入れます。
  5. 一時配列内の要素を元の配列にコピーします。

        ソートをカウントする時間計算量は O(n+k) です。ここで、n は配列の長さ、k はソートされる配列の最大値です。カウントソートのパフォーマンスは非常に効率的ですが、特定の条件を満たす必要があります。

//计数排序
void countingSort(int array[], int size) {
    int maxElement = array[0];
    for (int i = 1; i < size; i++) {
        if (array[i] > maxElement) {
            maxElement = array[i];
        }
    }

    int countArray[maxElement + 1] = {0};

    for (int i = 0; i < size; i++) {
        countArray[array[i]]++;
    }

    for (int i = 1; i <= maxElement; i++) {
        countArray[i] += countArray[i - 1];
    }

    int sortedArray[size];

    for (int i = size - 1; i >= 0; i--) {
        sortedArray[countArray[array[i]] - 1] = array[i];
        countArray[array[i]]--;
    }

    for (int i = 0; i < size; i++) {
        array[i] = sortedArray[i];
    }
}

9. バケットソート

        バケット ソートは非比較ソート アルゴリズムであり、特定の範囲内の浮動小数点数をソートするのに適しています。ソート対象の配列をいくつかのバケットに分割し、各要素を対応するバケットに割り当て、次に各バケット内の要素をソートし、最後にすべてのバケット内の要素を取り出して順序付けされた配列を取得します。

バケット並べ替えの具体的な手順は次のとおりです。

  1. バケットの数を決定します。各バケットは範囲間隔を表します。
  2. 並べ替える配列を走査し、各要素を対応するバケットに入れます。
  3. 各バケット内の要素を並べ替えるには、挿入並べ替えやクイック 並べ替えなどの並べ替えアルゴリズムを使用できます。
  4. バケットの順序に従って、バケット内の要素を 1 つずつ取り出して、順序付けられた配列を取得します。

        バケットの並べ替えの時間計算量は、バケットの数と並べ替えアルゴリズムのパフォーマンスによって異なります。場合によっては、バケット ソートにより線形の時間計算量を実現できます。

//桶排序
#include <iostream>
#include <vector>
#include <algorithm>

void bucketSort(float array[], int size) {
    // 创建桶
    std::vector<float> buckets[size];

    // 将元素分配到对应的桶中
    for (int i = 0; i < size; i++) {
        int bucketIndex = size * array[i];
        buckets[bucketIndex].push_back(array[i]);
    }

    // 对每个桶中的元素进行排序
    for (int i = 0; i < size; i++) {
        std::sort(buckets[i].begin(), buckets[i].end());
    }

    // 合并所有的桶
    int index = 0;
    for (int i = 0; i < size; i++) {
        for (float element : buckets[i]) {
            array[index++] = element;
        }
    }
}

10. 基数ソート

        基数ソートは、数値を各桁ごとにソートする非比較ソート アルゴリズムです。基数ソートの基本的な考え方は、数値を最下位の桁 (1 の桁) から最高の桁 (上位の桁) まで順番に並べ替えることです。各ラウンドは現在の桁の値に従ってバケットに分割され、その後、数値がバケットの順序に従って並べ替えられます。複数回のソートを経て、最終的に順序付けられた配列が得られます。

基数ソートの具体的な手順は次のとおりです。

  1. 最大桁数に基づいてソートのラウンド数を決定します。
  2. バケット化操作はラウンドごとに実行され、現在のビットの値に従って対応するバケットに数値が入れられます。
  3. 数値をバケットの順序に並べ替えて、新しい配列を取得します。
  4. すべてのビットが走査されるまで、上記の手順を繰り返します。

        基数ソートの時間計算量は O(d * (n + k)) です。ここで、n は配列の長さ、d は最大桁数、k は基数です。基数ソートは通常、整数型データを処理するために使用されます。

//基数排序
int getMax(int array[], int size) {
    int maxElement = array[0];
    for (int i = 1; i < size; i++) {
        if (array[i] > maxElement) {
            maxElement = array[i];
        }
    }
    return maxElement;
}

void countingSort(int array[], int size, int exp) {
    int output[size];
    int count[10] = {0};

    for (int i = 0; i < size; i++) {
        count[(array[i] / exp) % 10]++;
    }

    for (int i = 1; i < 10; i++) {
        count[i] += count[i - 1];
    }

    for (int i = size - 1; i >= 0; i--) {
        output[count[(array[i] / exp) % 10] - 1] = array[i];
        count[(array[i] / exp) % 10]--;
    }

    for (int i = 0; i < size; i++) {
        array[i] = output[i];
    }
}

void radixSort(int array[], int size) {
    int maxElement = getMax(array, size);

    for (int exp = 1; maxElement / exp > 0; exp *= 10) {
        countingSort(array, size, exp);
    }
}

おすすめ

転載: blog.csdn.net/crr411422/article/details/131194323