• 算法导论学习之——堆排序


        在这一篇文章中,首先介绍一下堆的属性和性质。然后讲解一下建堆的过程,最后讲解堆排序。


    1、堆的介绍

        堆的物理存储结构就是一个一维的数组,其数据结构就是一个完全的二叉树。需要注意的是堆中的每个结点不需要后继指针,其父节点和左右孩子结点都可以通过计算得到。假设要计算结点i(i为数组的下标)的父节点和左右孩子结点,可以使用以下公式计算:


    在此计算的都是结点的数组下标,

    由于堆的数据结构是一个完全二叉树,设该完全二叉树有n个结点。则其内部结点下标分别为:1,2,。。。

    故其叶子结点下标分别为:

    堆高度:就是从根节点到最长叶子结点的距离。包含N个结点的堆高为:

    其次是最大堆和最小堆问题,最大堆就是对任意的父节点的值总是大于或等于孩子节点的值,这样的话就能保证根节点保存堆中的最大元素。

    即:A[parent(i)] >=A[i]    (最大堆)

    同样,最小堆就是对任意的父节点的值小于或等于孩子结点的值,这样就能保证根节点的值最小。

    即:A[parent(i)] <=A[i]  (最小堆)

    一般情况下,最大堆用于堆排序中,最小堆用于优先队列中。

    2、建堆

    根据堆的性质,我们最终要得到堆的根节点是一个最值,因而,我们需要自底向上进行建堆,若自顶向下建堆的话则不能保证根节点是最值。在此仅讨论建立最大堆的情况。

    首先我们可以认为每个叶子结点已经是一个最大堆,然后从最末一个非叶子结点开始进行对调整,以满足最大堆的性质。直到到达第一个结点即根节点。

    在建堆中,主要有二个过程,一个是堆调整,另一个是建堆。在此,首先介绍一下对调整的过程,首先,假如要调整的结点为i,并且结点i的左右孩子已经都是最大堆。于是,我们首先计算A[i],A[ left(i) ],A[ right(i) ]的大小。1)若A[i]最大,则无需调整。2)若A[ left(i) ]最大,则将A[i]与A[ left(i) ]交换,此时最大值成为了父节点,而A[ i]到了原来的A[ left(i) ]的位置。然后又将A[ i ]与其左右孩子进行比较,直到叶子结点。同样,若A[right(i) ]最大,其操作同理可得。

    void max_heapify(int A[],int i)
    {
    	l=2*i;		//l = left(i)
    	r=2*i+1;		//r = right(i)
    	//heapSize为堆中实际元素的个数 
    	if(l <= heapSize && A[l] > A[i])	//largest为i和l中的最大下标值 
    		largest = l;
    	else largest = i;
    	if(l <= heapSize && A[r] > A[largest])
    		largest = r;
    	
    	if(i != largest){
    		swap(A[i], A[largest]);		//交换 A[i]与 A[largest]
    		max_heapify(A, largest);
    	}
    }
    
    建堆的代码如下:

    void build_max_heapify(int A[])
    {
    	for(int i=A.length/2; i>=1; i--)
    	{
    		max_heapify(A, i);
    	}
    }
    

    简单估算一下:我们在调用max_heapify的时间复杂度为:O(lgn),调用build_max_heapify的次数为:O(n)。因而,整个建堆的时间复杂度为:O(nlgn)。

    不过这个时间复杂度不是渐进紧确的,因为在调用max_heapify时并不是所有的结点高度都为lgn。实际上大部分结点的高度都很小,更准确的说,高度为h的结点个数至多有个,而在高度为h的结点上运行max_heapify的代价为:O(h)。因此,我们可以计算build_max_heapify的总代价为:






    最后得到构造一个最大堆的时间复杂度为:O(n)。即在线性时间内,可以把一个无序数组构造成为一个最大堆。

    3、堆排序

        排序思想:首先使用build_max_heapify将数组A[1,2,…,n]建成最大堆,然后将A[1]与A[n]的互换,然后再使用max_heapify调整数组A[1,2,…,n-1]成最大堆,然后将A[1]与A[n-1]互换,依次类推,最终得到一个升序的数组A[1,2,…,n]。

    void heapSort()
    {
    	build_max_heapify(A);
    	int heapSize = A.length;	//heapSize=A.length-size
    	while(heapSize > 1)
    	{
    		swap(A[1], A[heapSize]);	//交换 A[1]与 A[heapSize]
    		heapSize --; 
    		max_heapify(A,1); 
    	} 
    }
    
    时间代价分析:build_max_heapify的时间代价是:O(n), 调用了n-1次的max_heapify,每次的时间为:O(lgn)。故heapSort的总时间复杂度为:O(nlgn)。


    不知道公式怎么加到文章中,这个暂时做成图片传上来了,导致文章的排版比较乱!


  • 相关阅读:
    Word Break
    Binary Tree Right Side View
    41. First Missing Positive
    2 Sum ,3 Sum, 3 Sum close
    216. Combination Sum III
    190. Reverse Bits
    143. Reorder List
    142. Linked List Cycle II
    Single Number i,ii,iii
    62. Unique Paths i & ii
  • 原文地址:https://www.cnblogs.com/liuwu265/p/4032144.html
Copyright © 2020-2023  润新知