Bootstrap

排序算法(3) C++

1. 堆排序(Heapsort)

原理

堆排序利用堆这种数据结构进行排序。堆是一种完全二叉树,满足堆的性质。堆排序的过程如下:

  1. 将待排序的序列构造成一个最大堆。
  2. 将堆顶元素(最大值)与堆尾元素交换。
  3. 重新调整堆,使其保持堆的性质,重复以上步骤。
#include <iostream>
#include <vector>
using namespace std;

void printfArray(const vector<int>& a) {
    for (int nums : a) {
        cout << nums << " ";
    }
    cout << endl;
}

void swap_Heap(vector<int>& array, int i, int index) {
    int temp = array[i];
    array[i] = array[index];
    array[index] = temp;
}

void Adjust_Heap(vector<int>& array, int i, int heapSize) {
    int maxIndex = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < heapSize && array[left] > array[maxIndex]) {
        maxIndex = left;
    }

    if (right < heapSize && array[right] > array[maxIndex]) {
        maxIndex = right;
    }

    if (maxIndex != i) {
        swap_Heap(array, maxIndex, i);
        Adjust_Heap(array, maxIndex, heapSize);
    }
}

void Build_Heap(vector<int>& array) {
    for (int i = (array.size() / 2 - 1); i >= 0; i--) {
        Adjust_Heap(array, i, array.size());
    }
}

vector<int> HeapSort(vector<int> array) {
    int len = array.size();
    if (len <= 1) return array;
    Build_Heap(array);
    cout << "构建最大堆后: ";
    printfArray(array);
    while (len > 0) {
        swap_Heap(array, 0, len - 1);
        cout << "交换堆顶和堆尾后: ";
        printfArray(array);
        len--;
        Adjust_Heap(array, 0, len);
        cout << "调整堆后: ";
        printfArray(array);
    }
    return array;
}

int main() {
    vector<int> data = {3, 5, 1, 10, 2, 7};
    cout << "堆排序结果: ";
    vector<int> sortedData = HeapSort(data);
    printfArray(sortedData);
    return 0;
}
构建最大堆后: 10 5 7 3 2 1 
交换堆顶和堆尾后: 1 5 7 3 2 10 
调整堆后: 7 5 1 3 2 
交换堆顶和堆尾后: 2 5 1 3 7 10 
调整堆后: 5 3 1 2 
交换堆顶和堆尾后: 2 3 1 5 7 10 
调整堆后: 3 2 1 
交换堆顶和堆尾后: 1 2 3 
调整堆后: 2 1 
交换堆顶和堆尾后: 1 2 
堆排序结果: 1 2 3 5 7 10 

优缺点

  • 优点:时间复杂度为 O(nlog⁡n)O(n \log n)O(nlogn),空间复杂度为 O(1)O(1)O(1)。
  • 缺点:常数因子较大,速度可能较慢。

2. 计数排序(Counting Sort)

原理

计数排序是一种非比较排序算法,适合于范围较小的整数排序。它通过计数每个元素出现的次数,然后根据计数的结果直接构建已排序的数组。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void printfArray(const vector<int>& a) {
    for (int nums : a) {
        cout << nums << " ";
    }
    cout << endl;
}

vector<int> CountingSort(vector<int>& array) {
    int n = array.size();
    if (n == 0) return array;

    int maxVal = *max_element(array.begin(), array.end());
    vector<int> count(maxVal + 1, 0);

    for (int num : array) {
        count[num]++;
    }

    int index = 0;
    for (int i = 0; i < count.size(); i++) {
        while (count[i]-- > 0) {
            array[index++] = i;
            cout << "填充数组时: ";
            printfArray(array);
        }
    }
    return array;
}

int main() {
    vector<int> data = {4, 2, 2, 8, 3, 3, 1};
    cout << "计数排序结果: ";
    vector<int> sortedData = CountingSort(data);
    printfArray(sortedData);
    return 0;
}
填充数组时: 1 2 2 8 3 3 1 
填充数组时: 1 2 2 8 3 3 1 
填充数组时: 1 2 2 3 3 3 1 
填充数组时: 1 2 2 3 3 4 1 
填充数组时: 1 2 2 3 4 8 1 
计数排序结果: 1 2 2 3 3 4 8 

优缺点

  • 优点:时间复杂度为 O(n+k)O(n + k)O(n+k),适合处理范围小的整数。
  • 缺点:不适合范围极大的数据(如 111 到 100000010000001000000),会消耗大量空间。

应用示例

计数排序适合用于考试成绩的排序,尤其是成绩范围有限时。

3. 桶排序(Bucket Sort)

原理

桶排序将数据分散到多个桶中,然后对每个桶内部的数据进行排序,最后再将桶中的数据合并。适用于均匀分布的数值。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void printfArray(const vector<int>& a) {
    for (int nums : a) {
        cout << nums << " ";
    }
    cout << endl;
}

vector<int> BucketSort(vector<int>& array, int bucketSize) {
    if (array.empty()) return array;

    int maxVal = *max_element(array.begin(), array.end());
    int minVal = *min_element(array.begin(), array.end());
    int bucketCount = (maxVal - minVal) / bucketSize + 1;

    vector<vector<int>> buckets(bucketCount);

    for (int num : array) {
        int index = (num - minVal) / bucketSize;
        buckets[index].push_back(num);
    }

    vector<int> sortedArray;
    for (auto& bucket : buckets) {
        sort(bucket.begin(), bucket.end());
        sortedArray.insert(sortedArray.end(), bucket.begin(), bucket.end());
        cout << "合并桶后: ";
        printfArray(sortedArray);
    }
    return sortedArray;
}

int main() {
    vector<int> data = {0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.32};
    cout << "桶排序结果: ";
    vector<int> sortedData = BucketSort(data, 0.1);
    printfArray(sortedData);
    return 0;
}
合并桶后: 0.17 0.21 
合并桶后: 0.17 0.21 0.26 
合并桶后: 0.17 0.21 0.26 0.32 
合并桶后: 0.17 0.21 0.26 0.32 0.39 
合并桶后: 0.17 0.21 0.26 0.32 0.39 0.72 
合并桶后: 0.17 0.21 0.26 0.32 0.39 0.72 0.78 
合并桶后: 0.17 0.21 0.26 0.32 0.39 0.72 0.78 0.94 
桶排序结果: 0.17 0.21 0.26 0.32 0.39 0.72 0.78 0.94 

优缺点

  • 优点:适合均匀分布的数据,时间复杂度为 O(n+k)O(n + k)O(n+k)。
  • 缺点:当数据分布不均匀时,性能可能下降。

4. 基数排序(Radix Sort)

原理

基数排序通过对每一位数进行排序,最终实现整体的有序。首先对个位进行排序,然后对十位进行排序,依此类推,直到最大位数。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void printfArray(const vector<int>& a) {
    for (int nums : a) {
        cout << nums << " ";
    }
    cout << endl;
}

vector<int> CountingSortForRadix(vector<int>& array, int exp) {
    vector<int> output(array.size());
    int count[10] = {0};

    for (int num : array) {
        count[(num / exp) % 10]++;
    }

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

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

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

vector<int> RadixSort(vector<int>& array) {
    int maxVal = *max_element(array.begin(), array.end());
    for (int exp = 1; maxVal / exp > 0; exp *= 10) {
        CountingSortForRadix(array, exp);
    }
    return array;
}

int main() {
    vector<int> data = {170, 45, 75, 90, 802, 24, 2, 66};
    cout << "基数排序结果: ";
    vector<int> sortedData = RadixSort(data);
    printfArray(sortedData);
    return 0;
}

优缺点

  • 优点:时间复杂度为 O(nk)O(nk)O(nk),适合大规模数据。
  • 缺点:需要额外的空间,且只适用于整数。
;