栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

堆排序的实现

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

堆排序的实现

结合文章 堆结构的实现 服用更佳。

1、堆排序的过程

堆排序的过程:(此处以大根堆为例)

  1. 先让整个数组变成大根堆结构,建立堆的过程:
    1)从上到下的方法,时间复杂度为 O ( N ∗ l o g N ) O(N * logN) O(N∗logN)
    2)从下到上的方法,时间复杂度为 O ( N ) O(N) O(N)
  2. 把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为 O ( N ∗ l o g N ) O(N * logN) O(N∗logN)
  3. 把堆的大小减小成 0 后,排序完成
2、代码实现

#include 
#include 
#include 
using namespace std;

void swap(vector &arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

void heapInsert(vector &arr, int ind) {
    while (arr[ind] > arr[(ind - 1) / 2]) {
        swap(arr, ind, (ind - 1) / 2);
        ind = (ind - 1) / 2;
    }
}


void heapify(vector &arr, int ind, int heapSize) {
    int left = ind * 2 + 1;
    while (left < heapSize) {
        int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
        largest = arr[largest] > arr[ind] ? largest : ind;

        if (largest == ind) break;

        swap(arr, largest, ind);
        ind = largest;
        left = ind * 2 + 1;
    }
}

//堆排序
void heapSort(vector &arr) {
    if (arr.size() < 2) return ;

    //建堆:
    //方法一——从上到小建堆 O(NlogN)
    //for (int i = 0; i < arr.size(); i++) { //O(N)
    //    heapInsert(arr, i); //O(logN)
    //}

    //方法二——从下往上建堆 O(N)
    for (int i = arr.size() - 1; i >= 0; i--) {
        heapify(arr, i, arr.size());
    }
	
    int heapSize = arr.size();
    swap(arr, 0, --heapSize);
    //O(NlogN)
    while (heapSize > 0) { //O(N)
        heapify(arr, 0, heapSize); //O(logN)
        swap(arr, 0, --heapSize); //O(1)
    }
}



//for test
void comparator(vector &arr) {
    sort(arr.begin(), arr.end());
}

vector generateRandomArray(int maxSize, int maxVal) {
    vector arr(rand() % (maxSize + 1));
    for (int i = 0; i < arr.size(); i++) {
        arr[i] = (rand() % (maxVal + 1)) - (rand() % (maxVal + 1));
    }
    return arr;
}

vector copyArray(vector &arr) {
    vector backUp(arr.size());
    for (int i = 0; i < arr.size(); i++) {
        backUp[i] = arr[i];
    }

    return backUp;
}

bool isEqual(vector &arr1, vector &arr2) {
    if (arr1.size() != arr2.size()) return false;
    for (int i = 0; i < arr1.size(); i++) {
        if (arr1[i] != arr2[i])
            return false;
    }
    return true;
}


void printArray(vector &arr) {
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    srand(time(0));
    int testTime = 500000;
    int maxSize = 100;
    int maxValue = 100;
    bool success = true;

    for (int i = 0; i < testTime + 1; i++) {
        vector arr1 = generateRandomArray(maxSize, maxValue);
        vector arr2 = copyArray(arr1);
        heapSort(arr1);
        comparator(arr2);
        if (!isEqual(arr1, arr2)) {
            success = false;
            break;
        }
        if (i && i % 1000 == 0) cout << i << " cases passed!" << endl;
    }

    cout << (success ? "Nice!" : "Fucking fucked!") << endl;

    vector arr = generateRandomArray(maxSize, maxValue);
    printArray(arr);
    heapSort(arr);
    printArray(arr);

    return 0;
}
3、 堆排序的时间复杂度

O ( N l o g N ) O(NlogN) O(NlogN)

4、问:为什么建堆的时候从下到上的时间复杂度为 O ( N ) O(N) O(N) ?

假设一棵树有 N N N 个节点:

  • 那么它的叶子节点(最底层)从数量级上来说有 N / 2 N/2 N/2 个,这些节点在 heapify 的时候只看了自己,所以总的操作次数是 N / 2 ∗ 1 N/2 * 1 N/2∗1
  • 倒数第二层节点从数量级上来说有 N / 4 N/4 N/4 个,每个节点在 heapify 时只看了 2 个节点,所以总的次数是 N / 4 ∗ 2 N/4 * 2 N/4∗2
  • 倒数第三层节点从数量级上来说有 N / 8 N/8 N/8 个, heapify 到底也就是3个节点,所以总的操作次数是 N / 8 ∗ 3 N/8 * 3 N/8∗3
  • 所以总的时间复杂度为: T ( N ) = N / 2 ∗ 1 + N / 4 ∗ 2 + N / 8 ∗ 3 + N / 16 ∗ 4 + . . . . . . T(N) = N/2 * 1 + N/4 * 2 + N/8 * 3 + N/16 * 4 + ...... T(N)=N/2∗1+N/4∗2+N/8∗3+N/16∗4+......
  • 而 2 ∗ T ( N ) = N + N / 2 ∗ 2 + N / 4 ∗ 3 + N / 8 ∗ 4 + . . . . . . 2 * T(N) = N + N/2 * 2 + N/4 * 3 + N/8 * 4 + ...... 2∗T(N)=N+N/2∗2+N/4∗3+N/8∗4+......
  • 错位相减得: 2 ∗ T ( N ) − T ( N ) = T ( N ) = N + N / 2 + N / 4 + N / 8 + N / 16 + . . . . . . 2 * T(N) - T(N) = T(N) = N + N/2 + N/4 + N/8 + N/16+ ...... 2∗T(N)−T(N)=T(N)=N+N/2+N/4+N/8+N/16+...... ,等比数列一定收敛于 O ( N ) O(N) O(N)
5、问:为什么从上往下和从下往上建堆,时间复杂度有如此的差别?

从上往下的时候,随着插入的节点越来越多,树越来越高,更多的节点需要调整的树高越来越高;而从下往上,需要承载的更多操作次数而调整的节点越来越少。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/875335.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号