三、堆排序(Heapsort),优先队列可以用于O(N log N)

三、堆排序(Heapsort)

优先队列可以用于O(N log N)

存储空间增加一倍


排序类别


排序方法


时间复杂度


时间复


杂度


空间复杂度


稳定性


复杂性


平均情况


最坏情况


最好情况


选择排序


堆排序


O(nlog2n)


O(nlog2n)


O(nlog2n)


O(1)


不稳定


较复杂

堆排序:堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

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

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

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

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

3.找到第二个非叶节点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.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

来自 <https://www.cnblogs.com/chengxiao/p/6129630.html>

堆排序源码:

    public class Heapsort{
        private static int leftChild(int i){//获得左儿子2*i+1
            return 2 * i + 1;
        }

        private static <AnyType extends Comparable < ? super AnyType >> void percDown (AnyType[] a,int i,int n){
            int child;
            AnyType tmp;

            for(tmp = a[i];leftChild(i) < n;i = child){//第一趟交换
                child = leftChild(i);
                if(child != n - 1 && a[child].compareTo(a[child + 1]) < 0)//如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
                    child++;
                if(tmp.compareTo(a[child]) < 0)//如果父结点的值已经小于孩子结点的值,则将child的值赋值给father
                    a[i] = a[child];
                else
                    break;
            }
            a[i] = tmp;
        }

        private <AnyType> void swapReference(AnyType[] a,int i,int n){
            AnyType tmp = a[i];//DeleteMix
            a[i] = a[n];
            a[n] = tmp;
        }

        public <AnyType extends Comparable <? super AnyType>> void heapsort(AnyType[] a){
            for(int i = a.length / 2 - 1;i >= 0;i--){
                percDown(a,i,a.length);//BuildHeap
            }
            for(int i = a.length - 1;i > 0;i--){
                swapReference(a,0,i);
                percDown(a,0,i);
            }
        }

        public static <AnyType> void printPart(AnyType[] a,int begin,int end){
            for(int i = 0;i < begin;i++){
                System.out.print("\t");
            }
            for(int i = begin;i <= end;i++){
                System.out.print(a[i] + "\t");
            }
            System.out.println();
        }
        @Test
        public void testHeapsort(){
            Heapsort heap = new Heapsort();
            Integer[] a = new Integer[]{81,94,11,96,12,35,17,95,28,58,41,75,15};
            System.out.print("排列前:\t");
            heap.printPart(a,0,a.length-1);
            heap.heapsort(a);
            System.out.print("排序后:\t");
            heap.printPart(a,0,a.length-1);
        }
}


原文地址:https://www.cnblogs.com/PureJava/p/10592080.html

时间: 2024-11-09 03:09:05

三、堆排序(Heapsort),优先队列可以用于O(N log N)的相关文章

堆排序与优先队列&mdash;&mdash;算法导论(7)

1. 预备知识 (1) 基本概念     如图,(二叉)堆一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组A包括两个属性:A.length给出了数组的长度:A.heap-size表示有多少个堆元素保存在该数组中(因为A中可能只有部分位置存放的是堆的有效元素).     由于堆的这种特殊的结构,我们可以很容易根据一个结点的下标i计算出它的父节点.左孩子.右孩子的下标.计算公式如下: parent(i) =

堆排序、优先队列

1.堆排序 a.堆的定义 n个元素序列{k1,k2,...,kn}当且仅当满足以下关系时,称之为堆. ki<=k2i且ki<=k2i+1 (小根堆) ki>=k2i且ki>=k2i+1 (大根堆) 以下针对最大堆 b.维护堆的性质 Max-Heapify通过让A[i]的值在最大堆中"逐级下降"(A[i]的值小于其左右孩子的值时),从而使得以i为根结点的子树重新遵循最大堆性质. Max-Heapify(A,i) l = left(i) r = right(i) i

【组队赛三】-D 优先队列 cf446B

DZY Loves Modification Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 446B Description As we know, DZY loves playing games. One day DZY decided to play with a n?×?m matrix. To be more precise,

堆数据结构+堆排序+最大优先队列的堆的实现

对于堆排序,首先要先知道什么是堆数据结构,堆数据结构就是一个完全二叉树,但是它有自己的性质. 例如最大堆的性质为:A[PARENT[i]]>=A[i]:即每一个结点的值大于等于其左右孩子的值,小于等于其父节点的值.我们在这里只讨论最大堆的情况.我们知道一颗完全二叉树对应一个最大堆的形式,我们要做的就是将二叉树转化为最大堆,这就是所谓的最大堆的维护,我们定义函数MaxheapFY(A,i)来进行操作. 代码: /** *MaxheapFY(A,i):维护位置i最大堆性质,此时假设left(i)和r

堆排序HeapSort

堆排序,顾名思义,是采用数据结构堆来进行排序的一种排序算法. 研究没有规律的堆,没有任何意义.特殊的堆有最大堆(父节点值大于等于左右字节点值),最小堆(父节点值小于等于子节点值).一般采用最大堆来进行排序,图1为最大堆来表示一维数组. 图1 最大堆表示一维数组 2叉树堆的几点特性 1. 最后父节点索引值 不妨设堆的总元素个数为N:最后一个父节点的索引值Index = N/2 :可以写几个简单的堆进行数学归纳.图1中的最后一个父节点5 = 10 /2 : 这个特性主要是在采用自底向上构建堆的时候,

排序算法之堆排序(优先队列)

1.堆排序的堆,其实是一个 完全二叉树.既是一个结点要么是叶子结点,要么必定有左右两个子节点的树. 2.堆有序:每个结点的值,都必须大于两个子节点.但是两个子结点的大小不作要求. 3.一棵大小为N的完全二叉树,高度为lgN(层). 用数组实现堆,假设数组下标从0开始,下标为k的元素,它的左子树是2k+1,右子树是左子树+1,即2k+2 一:由上至下的有序化(下沉) 如果堆的有序状态,因为某个结点比它的两个子结点或者其中之一小而打破了,那么可以通过与两个子结点中的较大者来交换. 交换后可能会在子结

堆排序 Heapsort

堆排序: 1 #include<stdio.h> 2 //#include<stdlib.h> 3 4 void PrintArray(int data[] ,int length){ 5 int i; 6 for(i=0;i<length;i++){ 7 printf("%d ",data[i]); 8 } 9 printf("\n"); 10 } 11 12 //heap_size;堆的大小 13 // 堆化,保持堆的性质 14 /

堆排序——HeapSort

基本思想: 图示: (88,85,83,73,72,60,57,48,42,6) 平均时间复杂度: O(NlogN)由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN).二次操作时间相加还是O(N * logN). Java代码实现: public class HeapSortTest { public static void main(String[] args) { // TODO Auto

POJ 2051 argus(简单题,堆排序or优先队列)

又是一道数据结构题,使用堆来进行权值调整和排序,每次调整都是o(n)的复杂度,非常高效. 第一眼看题觉得可以用优先队列来做,应该也很简单. 事实上多数优先队列都是通过堆来实现的. 写的时候还是出了一些问题: 1.二叉树根节点下标显然不能为0: 2.限界之后若出现扩界要小心: 3.在迭代循环比较的时候,尤其注意到底比较的是谁,别把自己绕晕了. ac代码: #include<iostream> #include<cstdio> #include<cstdlib> #incl