• 堆排序


    C++完整代码

    特点:

    1. 与合并排序类似,运行时间$O(nlgn)$,比插入排序快

    2. 与插入排序类似,堆排序为in-place排序算法,空间复杂度$O(1)$,在任何时候,数组只有常熟个元素存储在输入数组以外。

    最大堆如图所示:

    堆可以用数组来进行实现。树的根为A[1] ,给定了某个节点i ,其父节点 parent(i) ,左子节点 left(i) 和右子节点 right(i) 在数组中的下标可以简单计算如下:

    [公式]

    [公式]

    [公式]

    用0作为数组起始下标,python实现如下所示

    def left(i):
        return 2 * (i + 1) - 1
    
    def right(i):
        return 2 * (i + 1)
    
    def parent():
        return (i + 1) * 2 - 1

    二叉堆有两种,最大堆和最小堆。最大堆是指父节点比子节点大,最大元素存放在根节点,并且在以一个节点为根的子树中,各节点的值都不大于该子树根节点的值。

    [公式]

    最小堆恰好相反: [公式]

    以最大堆为例:保持堆的性质

    例如下图中,对 $A[2]$ 进行check, $max{ 4, 14, 7} = 7$,因此$A[2]$ ,$A[4]$ 进行switch,然后沿着$A[4]$的子节点进行递归, $max{4,2,8}=8$,然后再进行switch,直到碰到叶子节点。

    在检查的时候只需要switch $A[i], A[largest]$,然后沿着被switch的节点(位置为largest的节点)为根的子树进行递归检查,另一个子树不需要检查。

    建堆

    首先给出一个问题并进行证明:当用数组表示存储了n个元素的堆时,叶子节点的下标是[公式]

    给定一个不明顺序的数组表示的堆,如何构造最大堆?

    def build_max_head(A):
        i = len(A) / 2 - 1
        while i >= 0:
            max_heapify(A, i)
            i -= 1
    
    
    if __name__ == '__main__':
        A=[1,2,3,4,5]
        build_max_head(A)
        print A
    
    输出结果为:
    [5, 4, 3, 1, 2]

    如上所述,对所有的非叶子节点,自下往上地调用MAX-HEAPIFY,始终维持着子树的最大堆属性,因此到根节点,整个树就成为最大堆。

    构建堆的时间复杂度为$O(n)$。

    堆排序算法HEAP-SORT

    堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

    def head_sort(A):
        result = []
        build_max_head(A)
        while len(A) > 1:
            result.append(A[0])
            A[0],A[len(A)-1] = A[len(A)-1],A[0]
            A = A[:-1]
            max_heapify(A,0)
        result.extend(A)
        return result
    
    if __name__ == '__main__':
        A=[1,2,3,4,5,7,8,10,400]
        result = head_sort(A)
        print result
    
    输出结果为:
    [400, 10, 8, 7, 5, 4, 3, 2, 1]

    算法步骤如下:

    1. 首先对数组进行建堆,这样得到最大堆
    2. 取堆的根节点,也就是最大值
    3. 保持树的结构不变,将根节点与最后一个值交换,然后对根节点进行MAX-HEAPIFY,这样第二大的值就成为根节点,因此类推

    这里需要注意两点:

    1. 实现的时候才用了额外的list来存放,也可以采用额外的变量heap-size来进行处理,每次只处理 [公式] 范围的数据,这样就是完全的in-place了
    2. 循环过程中,每次都将 [公式] 进行交换,这样就不会修改树的结构,然后直接进行MAX-HEAPIFY

    完整示例 

    步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

    假设给定无序序列结构如下

    此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 [数组长度/2]=[5/2]=1,也就是下面的6结点  []为向下取整),从左至右,从下至上进行调整。

    第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

    这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

    此时,我们就将一个无需序列构造成了一个大顶堆。

    步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

    a.将堆顶元素9和末尾元素4进行交换

    b.重新调整结构,使其继续满足堆定义

    c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

    后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

    再简单总结下堆排序的基本思路:

      a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

      b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

      c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

     

    完整代码 C++

    void buildMaxHeap(int A[], int n)  //建立最大堆
    
    {
    // 从最后一个非叶子节点(n/2-1)开始自底向上构建,
        for (int i = n / 2-1; i >= 0; i--)  //从(n/2-1)调用一次maxHeapIfy就可以得到最大堆
    
             maxHeapIfy(A, i, n);
    
    }
    
    void maxHeapIfy(int A[], int i, int n)  //将i节点为根的堆中小的数依次上移,n表示堆中的数据个数
    
    {
        int l = 2 * i + 1;   //i的左儿子
    
        int r = 2 * i + 2;  //i的右儿子
    
        int largest = i;   //先设置父节点和子节点三个节点中最大值的位置为父节点下标
    
        if (l < n && A[l] > A[largest])
    
             largest = l;
    
        if (r < n && A[r] > A[largest])
    
             largest = r;
    
        if (largest != i)    //最大值不是父节点,交换
    
        {
    
             swap(A[i],A[largest]);
    
             maxHeapIfy(A, largest, n);  //递归调用,保证子树也是最大堆
    
        }
    }
    
    void heapSort(int A[], int n)  //堆排序算法
    
    {
        buildMaxHeap(A, n);  //先建立堆
    
        for (int i = n-1; i >0; i--)
    
        { 
    // 将根节点(最大值)与数组待排序部分的最后一个元素交换,这样最终得到的是递增序列
             swap(A[0], A[i]);
    // 待排序数组长度减一,只要对换到根节点的元素进行排序,将它下沉就好了。
             maxHeapIfy(A, 0, i);
        }
    }

    堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆复杂度为$O(n)$,在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为$nlogn$。所以堆排序时间复杂度一般认为就是$O(nlogn)$级。

    参考 1

    参考 2

    参考 3

     

  • 相关阅读:
    MapKit 大头针基本使用
    iOS获取文件的大小
    iOS截取图片方法
    iOS通知3种使用方法
    iOS12适配及兼容问题解决,xcode10问题
    Mac不使用iTunes导出照片
    Xcode 10 项目迁移 Multiple commands produce...
    tableView在UITableViewStylePlain的状态下取消悬浮效果(取消粘性效果)
    OC 字符串和字典的相互转化
    破解 Mac OS管理员密码 亲测可用
  • 原文地址:https://www.cnblogs.com/shona/p/12578429.html
Copyright © 2020-2023  润新知