数据结构-练习 12 堆以及最优队列的实现

堆排序是最重要的排序算法之一,在平时的开发以及面试中经常会用到。堆的特点是:

        1,有一颗完全二叉树构成,如图1;

         2,可分为最大堆和最小堆。最大堆的意思就是:任何根节点的数据不小于左右孩子节点的数据;反之,最小堆的意思就是任何节点的数据不大于左右孩子节点的数据;

         3,堆排序的算法复杂度为O(NlgN),比冒泡和插入快,究其原因在于堆只维护局部最大或最小。

         4,堆的存储用数组实现,按层存储,如图1;

                                         数据结构-练习 12 堆以及最优队列的实现_第1张图片 

                                                       存储在数组里:

                                                   

                                                         图 1

  下面分四部分讲解堆,分别为:堆的插入,删除,以及堆排序,最后是堆化的优先队列。

  一,堆的插入

  堆的插入只能在最后一个位置插入,如图 2。只能在黑色圈的地方插入。插入以后,必须进行更新,以保持堆的性质,即根数据最大或是最小。更新的思路:从插入节点到根节点,依次比较更新,直到满足条件(本列子就是:根数据小于左右孩子)。

                                                                大致原理如图解:

数据结构-练习 12 堆以及最优队列的实现_第2张图片

数据结构-练习 12 堆以及最优队列的实现_第3张图片

数据结构-练习 12 堆以及最优队列的实现_第4张图片

数据结构-练习 12 堆以及最优队列的实现_第5张图片

数据结构-练习 12 堆以及最优队列的实现_第6张图片

关键代码如下:

            注意的是:左孩子和右孩子的顺序分别为2i+1,2i+2。结合上面的流程图,插入的过程为:从下往上依次比较,重新调整堆的结构。

void MinHeapFixup(int* a, int i)  
{  
    int j, temp;       
    temp = a[i];  
    j = (i - 1) / 2;      //父结点  
    while (j >= 0 && i != 0)  
    {  
        if (a[j] <= temp)  
            break;  
          
        a[i] = a[j];     //把较大的子结点往下移动,替换它的子结点  
        i = j;  
        j = (i - 1) / 2;  
    }  
    a[i] = temp;  
}  


void MinHeapAddNumber(int* a, int n, int nNum)  
{  
    a[n] = nNum;  
    MinHeapFixup(a, n);  
}  
二,堆的删除

记住只能删除最顶部的元素,再把最低端的元素放到最顶端,从上往下依次更新整个堆。示意图如下:




数据结构-练习 12 堆以及最优队列的实现_第7张图片 数据结构-练习 12 堆以及最优队列的实现_第8张图片
数据结构-练习 12 堆以及最优队列的实现_第9张图片
数据结构-练习 12 堆以及最优队列的实现_第10张图片
void MinHeapFixdown(int a[], int i, int n)  
{  
    int j, temp;  
  
    temp = a[i];  
    j = 2 * i + 1;  
    while (j < n)  
    {  
        if (j + 1 < n && a[j + 1] < a[j]) //如果存在有孩子,比较左右孩子的大小
            j++;  
  
        if (a[j] >= temp)  
            break;  
  
        a[i] = a[j];     
        i = j;  
        j = 2 * i + 1;  
    }  
    a[i] = temp;  
}  

void MinHeapDeleteNumber(int a[], int n)  
{  
	std::swap(a[0], a[n - 1]);  
    MinHeapFixdown(a, 0, n - 1);  
}  

总结:删除,只能删除头结点,然后从上往下依次调整堆;插入,只能在末端插入,然后从下往上依次调整堆。
三,堆化数组,采用插入的思想从下往上依次调整堆即可。此时调整每一个非叶子节点即可。
关键代码如下:
void MakeMinHeap(int* a, int n)  
{  
    for (int i = n / 2 - 1; i >= 0; i--)  
    MinHeapFixdown(a, i, n);  
}  
测试:
结合图形中的数据,我们演示一遍:

int main()
{
	int a[8]={9,7,5,6,8,4,10};
	cout<<"原始数组里的数据为:";
	for(int i=0;i<7;++i)
		cout<




我们自然要问:维护一个堆结构的意义何在?
至少有两个目的:堆排序和优先队列。
四 ,堆排序。思想:既然我们可以每次取出顶点的数据,取到的数据又是最小数据,根据此思想,我们可以把数组里的数据全部取完后得到的数据就是有顺序的了。不过跟归并排序一样,空间复杂度增加了,必须有一个数组去接受。
测试:
int b[8];
    for(int i=0;i<8;++i)
	{
		b[i]=a[0];
	    MinHeapDeleteNumber(a,8-i);
	}
	for(int i=0;i<8;++i)
		cout<


我们知道,任何算法都得用时间复杂度计算一下,否则,算法没什么意义。由于按树堆化,树高lgN,最坏情况下,每层计算N次,所以复杂度为:O(NlgN),跟归并一样。
最优队列:最优队列是每次都弹出数据的最大或是最小值。每次进队列后,都要一次更新操作。每次出队列都要一次删除操作。

你可能感兴趣的:(数据结构,数据结构,算法,堆,堆排序,优先队列)