堆-优先队列

来源:互联网 发布:冲突世界 知乎 编辑:程序博客网 时间:2024/06/15 15:37

面试的时候被问到了heapsort,发现自己对数据结构这块的理解实在不够,最近打算推翻重新来学一遍。

这里先简单地说一下自己对堆的理解。

堆的特质:

1.平衡二叉树=》保证了操作时间复杂度为logN

2.以最小堆为例,对任意结点,其父节点均小于子节点。=》这样几乎各种操作都是在纵向上(深度)进行的。


由于在堆这个数据结构中,经常需要用到父节点和子节点这个概念,因而为了方便父节点与子节点的取出,当我们把堆存储在数组中时,设置从初始下标由1开始,这样父节点的下标就是子节点下标的一半了。

eg:assume the index of parent is i, then the index of left child is 2*i,and thus right child's 2*i+1


下面的问题:

给一个乱序数组,如何对其进行排序使其满足堆的性质?


我们先把问题化小:给定一个父节点,若已知其左右节点形组成的二叉树满足堆性质,那该如何调节,使得这个父节点组成的二叉树也满足堆性质。

1.若父节点均小于子节点,那么这个父节点以下的堆便满足堆性质。

2.若父节点大于任意一个子节点,那么我们需要交换父子结点,这样引起了一个问题,这个操作会不会打破被交换的子节点以下的堆的性质?如果父节点过大,大于子节点之下的孩子,那么我们还需要对子节点以下的堆进行调整,使其满足堆性质。这样,递归调整下去,直至父节点以下的所有结点都满足堆性质。

void min_heapify(int arr[],int arr_size,int parent){    int left_child=2*(parent+1)-1;    int right_child=2*(parent+1);    if(left_child>=arr_size)        return;    int small;    int tmp;    if(right_child<arr_size&&arr[left_child]>arr[right_child])        small=right_child;    else        small=left_child;    if(arr[small]<arr[parent]){        tmp=arr[parent];        arr[parent]=arr[small];        arr[small]=tmp;        min_heapify(arr,arr_size,small);    }    return;}



回到先前的问题中,我们想构造出一个情景使得我们先前想出的解决方案可以嵌入使用。

我们发现,若我们由下至上地调整结点使其满足堆性质,可以满足我们之前的假设,对给定的一个父节点,其子节点满足堆性质。

那我们就从最后一个父节点推到最前。


如何得到最后一个父节点的下标:

在二叉树中,假设叶节点(度为零的点)共有i个,则度为2点共有i-1个。

证明:

假设二叉树的0度,1度,2度结点为n0,n1,n2,总节点数为T
则有按照结点求和的
T = n0 + n1 + n2       (1)
按照边求和得:
T = n1 + 2 * n2 + 1   (2)
所以 (2) - (1)可得
n2 + 1 - n0 = 0
所以n0 = n2 + 1


当度为1的点有一个时,共有2*i个点,第一个叶节点为第i+1个。设n=2*i,则i+1=n/2+1。

当度为1的点有零个时,共有2*i-1个点,第一个叶节点为第i个。设n=2*i-1,则i=(n+1)/2。

可知,假设总结点数为n,当总结数为偶数时,第n/2+1个为叶节点。当总结点数为奇数时,第(n-1)/2+1=floor(n/2)+1个为叶节点

则我们可以得到第floor(n/2)+1为第一个叶节点,则最后一个父节点为floor(n/2)


void build_heap(int arr[],int arr_size){    int i=floor(arr_size/2);    for(i;i>0;i--){        min_heapify(arr,arr_size,i-1);    }}

如何进行heapsort:

我们需要将数组分割开来,一部分为继续需要排序的heap,一部分为已经排序好的数组。

在先前的操作中我们可以看到,对于结点的选取调整是基于父节点和子节点下标为一半的关系,所以我们需要传入的heap初始下标最好为1(或者0),这样,我们将每次提取的最小节点放在最后(数组的后半部分),而将需要继续调整的heap放在前面。

void heap_sort(int arr[],int arr_size){    build_heap(arr,arr_size);    int tmp;    for(int i=arr_size-1;i>0;i--){        tmp=arr[0];        arr[0]=arr[i];        arr[i]=tmp;        min_heapify(arr,i,0);    }}


由此我们得到了由大至小的数组排序。
0 0
原创粉丝点击