• 经典算法之堆排序及优先队列


    活动地址:CSDN21天学习挑战赛

    📚“九层之台,起于垒土”。学习技术须脚踏实地。

    ☀️你要做冲出的黑马,而不是坠落的星星。
    📖这里推荐一款刷题、模拟面试神器,可助你斩获心仪大厂offer:点我免费刷题、模拟面试

    优先队列

    支持删除最大元素和插入元素的数据类型叫做优先队列

    优先队列的操作

    1. 向优先队列中插入一个元素。
    2. 返回最大元素。
    3. 删除并返回最大元素。
    4. 返回队列中元素的个数。
    5. 返回队列是否为空。

    优先队列的实现

    1. 数组或链表的有序实现

    对于数组来说,将最大的元素置于数组尾部,每次插入遍历数组,将所有较大的元素向移动,然后将要插入的元素置于应在的位置,就像插入排序一样。删除则仅需要将最大的元素删去即可。
    对于链表,将最大的元素置于表头,每次插入遍历,找到对应结点插入,此过程需要维护两个指针,用于比较的指针与这个指针的前一个指针,用于插入。

    2. 数组或链表的无序实现

    对于数组来说,每次插入就将元素置于数组尾部,删除时则遍历数组,找到最大的与数组尾部的元素交换,并返回。
    对于链表,每次插入将元素置于表头,删除时遍历,同样维护两个指针。

    3. 二叉堆实现

    当一棵二叉树的每个结点都大于等于它的两个子结点时,它被称为堆有序
    二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存。

    算法实现:

    1. 插入节点时,先将其插入数组的尾部,让其上浮(swim),直至到达根节点或小于等于父节点。
    2. 删除节点时,将数组第一个元素与最后一个元素交换,让第一个节点下沉(sink),直至到达叶子节点或大于子节点。

    C++实现:

    下面的算法将数组索引为 0 的位置空了出来,数组的长度为能存储最大元素数量加一。

    template<typename T>
    void sink(T* a, int k, size_t size){
        while(2*k <= size){
            int j = 2*k;
            if(j < size && a[j] < a[j + 1])j++;
            if(a[k] > a[j]) break;
            swap(a[k], a[j]);
            k = j;
        }
    }
    
    template<typename T>
    void swim(T* a, int k){
        while(k > 1 && a[k/2] < a[k]){
            swap(a[k/2], a[k]);
            k = k/2;
        }
    }
    
    template<typename T>
    void insert(T* a, T t, int &size){
        a[++size] = t;
        swim(a, size);
    }
    
    template<typename T>
    T delMax(T* a, int &size){
        T max = a[1];
        swap(a[1], a[size--]);
        sink(a, 1, size);
        return max;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.三种实现的最坏复杂度分析

    通过上面的分析,不难得出:

    数据结构插入元素删除最大元素
    有序数组或链表N1
    无序数组或链表1N
    logNlogN

    堆排序的概念

    我们可以把优先队列变成一种排序方法,每次选取最大的元素,不断构造堆,就可以得到有序序列。

    算法描述

    • 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
    • 将根节点与末尾节点交换。
    • 交换后的根节点下沉,直到满足堆定义。

    C++实现

    template<typename T>
    void sink(T* a, int k, size_t size){
        while(2*k <= size){
            int j = 2*k;
            if(j < size && a[j - 1] < a[j])j++;
            if(a[k - 1] > a[j - 1]) break;
            swap(a[k - 1], a[j - 1]);
            k = j;
        }
    }
    
    template<typename T>
    void heapsort(T *a, size_t N){
        for (int k = N / 2; k >= 1; k--) {
            sink(a, k, N);
        }
        while (N > 1) {
            swap(a[0], a[--N]);
            sink(a, 1, N);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    复杂度分析

    • 时间复杂度:
      • 比较时间:建堆是通过父节点和子节点两两比较并交换得到的,时间复杂度为O(n)。
      • 交换时间:调整堆需要交换n-1次堆顶元素,并调整堆,调整堆的过程就是满二叉树的深度logn。
        所以时间复杂度为O(nlogn)。
    • 空间复杂度:需要几个变量存储索引,所以为O(1)。

    🚩本周的学习就到此结束啦,本周仍以复习经典排序算法为主,学习算法过程中动图有助于我们了解算法执行的过程当然自己画一遍试试坑会理解的更深。
    ✨要想超越别人的脚步,掌握比别人更多的知识!那就快来刷题吧:点我免费刷题、模拟面试

    🌏我是博仔,期待你的关注,让我们一起进步!

  • 相关阅读:
    快速识别你家的猫猫狗狗,教你用ModelBox开发AI萌宠应用
    46 二叉树展开为链表
    fastadmin中api模块自动validate验证参数
    关于信度分析的多种方法
    基于Beego 1.12.3的简单website实现
    Thinkphp6 模型 指定字段自增的方法
    开发多个项目之后的一些感想....
    createNodeIterator认识
    linux命令汇总
    C语言:数组指针
  • 原文地址:https://blog.csdn.net/weixin_45773137/article/details/126442329