标签:
主要内容:
1、什么是堆?
2、如何建堆
3、堆排序
4、参考代码
“堆”是个很有趣的数据结构,是个完全二叉树。
“堆”的特性:每个节点的键值一定总是大于(或小于)它的父节点(大于:称为“最大堆”,小于:称为“最小堆”),或者说每个节点总是大于或小于它的子节点。
对于最大堆而言,根节点为最大值;对于最小堆而言,根节点为最小值。
通常“堆”是通过数组来实现的,这样可以利用数组的特点快速定位指定索引的元素。
因此,当我们得知某个节点的索引为 i 时,可以通过以下“堆”的定义很容易地定位到它的子节点和父节点。
在起始索引为 0 的“堆”中:
1) 堆的根节点将存放在位置 0
2) 节点 i 的左子节点在位置 2 * i + 1
3) 节点 i 的右子节点在位置 2 * i + 2
4) 节点 i 的父节点在位置 floor( (i - 1) / 2 ) : 注 floor 表示“取整”操作
在起始索引为 1 的“堆”中:
1) 堆的根节点将存放在位置 1
2) 节点 i 的左子节点在位置 2 * i
3) 节点 i 的右子节点在位置 2 * i + 1
4) 节点 i 的父节点在位置 floor( i / 2 ) : 注 floor 表示“取整”操作
根据上述对于堆的定义,建立一个堆,首先需要构建一棵完全二叉树,树的节点需要满足:每个节点的值都不大于或不小于其父节点的值,即A[PARENT[i]] >=/<= A[i]。
给定一个无序数组,如何构建一个最大堆呢?
首先,将该无序数组构建为一棵完全二叉树,然后根据每个节点需要满足的条件,进行二叉树的调整。
建立一个最大堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。
调堆的过程应该从最后一个非叶子节点(最后一个节点的父节点)开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。
那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始,分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。
所以建堆的过程就是
for ( i = headLen/2-1; i >= 0; i++) ? do AdjustHeap(A, heapLen, i)
调堆:
如果初始数组是非降序排序,那么就不需要调堆,直接就满足堆的定义,此为最好情况,运行时间为Θ(1);
如果初始数组是如图1.5,只有A[0] = 1不满足堆的定义,经过与子节点的比较调整到图1.6,但是图1.6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。
如果递归调堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。
调堆的时间复杂度:O(logn)
建堆的时间复杂度:O(n)
关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html
根据“堆”的特性,我们可以从无序序列的最后一个节点的父节点开始,对其进行“下移”操作,直到序列的第一个节点为止。这样就可以保证每个节点都在合适的位置上,也就建立起了一个“堆”。
但是“堆”并不是一个完全排序的序列,因为“堆”只保证了父节点与子节点的位置关系,但并不保证左右子节点的位置关系。那么我们如何进行“堆排序”呢?
由于一个“堆”的根节点必然是整个序列中最大的元素,因此对于一个排序的序列而言,每个“堆”中我们只能得到一个有效的元素。如果我们拿掉根节点,再对剩下的序列重新排列组成一个“堆”,反复如此,我们就可以依次得到一个完整的排序序列了。
当然,为了简化操作,每次我们只需要把根节点与最后一个位置的节点交换,然后把最后一个位置排除之外,对根节点进行“下移”操作即可。
堆排序的时间复杂度:O(nlogn)
关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html
建堆完成之后,堆如图1.7是个大根堆。
将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。
#include <iostream> using namespace std; int leftChild(int i){ return 2*i+1; } int rightChild(int i){ return 2*i+2; } // adjust heap void makeMaxheap(int *A,int len,int i){ int left=leftChild(i); int right=rightChild(i); int largest=i; int tmp; while(left<len || right<len){ if(left<len && A[largest]<A[left]) largest=left; if(right<len && A[largest]<A[right]) largest=right; if(largest!=i){ tmp=A[largest]; A[largest]=A[i]; A[i]=tmp; i=largest; left=leftChild(i); right=rightChild(i); } else break; } } void makeMinheap(int *A,int len,int i){ int left=leftChild(i); int right=rightChild(i); int smallest=i; int tmp; while(left<len || right<len){ if(left<len && A[smallest]>A[left]) smallest=left; if(right<len && A[smallest]>A[right]) smallest=right; if(smallest!=i){ tmp=A[smallest]; A[smallest]=A[i]; A[i]=tmp; i=smallest; left=leftChild(i); right=rightChild(i); } else break; } } // build heap void buildMaxHeap(int* A,int len){ int begin=len/2-1; for(int i=begin;i>=0;i--) makeMaxheap(A,len,i); } void buildMinHeap(int* A,int len){ int begin=len/2-1; for(int i=begin;i>=0;i--) makeMinheap(A,len,i); } // heap sort void heapMaxSort(int* A,int len){ int hlen=len; int tmp; buildMaxHeap(A,hlen); while(hlen>0){ tmp=A[hlen-1]; A[hlen-1]=A[0]; A[0]=tmp; hlen--; makeMaxheap(A,hlen,0); } } void heapMinSort(int* A,int len){ int hlen=len; int tmp; buildMinHeap(A,hlen); while(hlen>0){ tmp=A[hlen-1]; A[hlen-1]=A[0]; A[0]=tmp; hlen--; makeMinheap(A,hlen,0); } } int main() { int A[]={1,3,4,5,7,2,6,8,0}; int len=sizeof(A)/sizeof(A[0]); buildMaxHeap(A,len); for(int i=0;i<len;i++) cout<<A[i]<<" "; cout<<endl; int B[]={1,3,4,5,7,2,6,8,0}; buildMinHeap(B,len); for(int i=0;i<len;i++) cout<<B[i]<<" "; cout<<endl; heapMaxSort(A,len); for(int i=0;i<len;i++) cout<<A[i]<<" "; cout<<endl; heapMinSort(B,len); for(int i=0;i<len;i++) cout<<B[i]<<" "; cout<<endl; return 0; }
http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html
http://wxg6203.iteye.com/blog/668968
标签:
原文地址:http://www.cnblogs.com/AndyJee/p/4612821.html