第三大题的应用题第一题 ①堆排序方法从小到大排序是啥意思?是要用小顶堆么?②重建堆是啥?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第三大题的应用题第一题 ①堆排序方法从小到大排序是啥意思?是要用小顶堆么?②重建堆是啥?相关的知识,希望对你有一定的参考价值。

第三大题的应用题第一题
①堆排序方法从小到大排序是什么意思?是要用小顶堆么?②重建堆是什么?本题答案怎么写

【概念】堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]]>=A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。【起源】1991年的计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德(RobertW.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了著名的堆排序算法(HeapSort)。【简介】堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。(1)用大根堆排序的基本思想①先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区②再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。……直到无序区只有一个元素为止。(2)大根堆排序算法的基本操作:①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2)其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)[2]注意:①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止【特点】堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录【算法分析】堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。平均性能:O(N*logN)。其他性能:由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。堆排序是就地排序,辅助空间为O(1)。它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前和排序后他们的相对位置不发生变化)。 参考技术A 安大数据结构期末题吗?这题就是要建大顶堆,我刚开始看答案也以为答案错了。但是你看严蔚敏数据结构的书第281页有这么一句话:使记录序列按关键字非递减有序排列,则在堆排序的算法中先建一个“大顶堆”。重建堆是指选大顶堆的最顶层的最大数与序列中最后一个数交换,然后再重新调整为大顶堆的过程

算法:堆排序


 

 

堆排序可归纳为两个操作:

1)建堆:根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。

2)调整堆:每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。 当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。调整堆的过程是:比较节点i和左右节点,选出三者最大的,如果是孩子节点,那么交换;并继续比较。

建大根堆:buildMaxHeap(a[])

从A.length / 2一直到根结点进行堆调整。

堆调整:adjustHeap(a[], parent, length);

  • 定义左孩子child = 2 * parent + 1;tmp保存父节点;
  • 当child < length 的时候:
    • 如果有右孩子并且右孩子大于左孩子,则选取右孩子;(选择孩子中较大的;)
    • 如果父节点值>孩子节点 break;(父>子,调整完毕;)
    • 孩子节点的值赋给父节点;(调整父和孩子;)
    • 孩子index赋给父index;并继续child的孩子index赋给孩子index(继续向下调整;)
  • tmp赋给最后的父节点;(找到调整值得最终位置;)

堆排序:heapSort(a[])

  • 建最大堆;
  • for(i=n-1)循环n-1次,交换首尾;adjustHeap(a, 0, i);

辅助交换:swap(a[], i, j)

 

参考代码:

public class Solution {
    /**
     * @param A an integer array
     * @return void
     */
     //堆排序
    public void sortIntegers(int[] a) {
        if (a.length == 0) return;
        buildMaxHeap(a);
        for (int i = a.length - 1; i > 0; i--) {
            swap(a, i, 0);
            adjustHeap(a, 0, i);
        }
    }
    
    public void buildMaxHeap(int[] a) {
        for (int i = a.length/2; i >= 0; i--) {
            adjustHeap(a, i, a.length);
        }
    }
    
    
    public void adjustHeap(int[] a, int parent, int length) {
        int tar = a[parent];
        int child = parent * 2 + 1;
        while (child < length) {
            if (child + 1 < length && a[child] < a[child + 1]) child++;
            if (a[child] < tar) break;
            a[parent] = a[child];
            parent = child;
            child = child * 2 + 1;
        }
        a[parent] = tar;
    }
    
    public void swap (int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
    
}

 

  • adjustHeap(a, parent, length):
    • tar值保存a[parent];定义child;
    • 当chile<length:
      • 获取左右孩子中较大的:若存在右孩子且大于左孩子则child++;
      • 比较child和tar:若已经满足[tar]>[child]则break;
      • child值赋给parent:注意这里是赋值而不是swap;
      • 继续向下调整:parent变化,child变化;
    • tar值赋给最终的a[parent];
  • buildMaxHeap(a):
    • for(i=a.length/2; i>=0),adjustHeap(a,i,a.length);注意参数是length;循环范围是[0,a.length/2];
  • heapSort(a):
    • 判断空return;
    • 建堆buildHeap(a);
    • 循环n-1次调整堆for(i=a.length-1; i>0); 循环范围是(0,length-1]n-1次;
      • swap(a,i,0);
      • adjustHeap(a,0,i) length的范围是[0,n-1]也就是i;
  • swap(a[],i,j);

 

参考:堆排序

测试数据:LintCode--Sort Integers 


 

以上是关于第三大题的应用题第一题 ①堆排序方法从小到大排序是啥意思?是要用小顶堆么?②重建堆是啥?的主要内容,如果未能解决你的问题,请参考以下文章

PAT甲题题解1098. Insertion or Heap Sort (25)-(插入排序和堆排序)

JAVA基础学习笔记_四种排序方法

[bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs

将五个数的序列排序,从小到大排列,有多少种排法?

算法:堆排序

java数组排序