堆
概念:
堆:从逻辑角度上来看是一棵完全二叉树,从物理结构来看,一般是顺序存储结构(数组)。
堆保证一种性质:堆中某个节点的值总是不大于或不小于其父节点的值; 我们通常使用的都是二叉树,二叉堆,因此用二叉堆进行讲解。
分类:大根堆(根结点的权值大于等于子结点的权值),小根堆。
基本数据结构:
- 因为堆是完全二叉树,完全能够使用数组来实现。
1 vector<int> heap; // 最好heap[0]插入一个不用的值,保持2n和2n+1的数学关系 2 int heap[maxn];
两个基本的操作:
- Top-Down调整堆结构。 将根结点和子结点中最大/小的值进行比较和替换。
- 时间复杂度:O(logN)
- 适用于建堆和删除堆中元素。
1 //对[low,high)进行调整; 2 //以大根堆为例,自顶向下进行调整 3 void DownAdjust(int low,int high){ 4 int i=low,j=i*2; 5 // 存在孩子结点 6 while(j<high){ 7 // 获得孩子结点中最大的值 8 if(j+1<high && heap[j+1]>heap[j]){ 9 j++; 10 } 11 // 如果孩子结点的值比父节点的值大,进行替换 12 if(heap[j]>heap[i]){ 13 swap(heap[i],heap[j]); 14 i=j; 15 j=i*2; 16 } 17 else{ 18 break; 19 } 20 } 21 }
- Down-Top调整堆结构。 只需要将插入的点和父结点进行比较。
- 适合于堆中插入元素。
1 void UpAdjust(int low,int high){ 2 int i=high,j=high/2; 3 while(i!=low){ 4 if(heap[j]<heap[i]){ 5 swap(heap[i],heap[j]); 6 i=j; 7 j/=2; 8 } 9 else{ 10 break; //只要有一个大于,后面一定不需要再次调整; 11 } 12 } 13 }
堆的操作:
- 建堆:如果有n个点,放入数组中,对下标为n/2(向下取整)的结点开始,到下标为1的点,进行调整堆结构。(从上至下)(保证每个小堆成立,然后再建立大堆)。 如果采用UpAdjust,采用后n/2元素进行建堆。
1 void CreateHeap(){ 2 for(int i=n/2;i>=1;i++){ 3 DownAdjust(i,n); 4 } 5 }
- 删除堆顶元素:将最后一个元素移到开头,让后重新调整一次堆,即可重新得到堆。 O(logN)
1 void DeleteTop(){ 2 heap[1]=heap[n--]; 3 DownAdjust(1,n); 4 }
- 插入结点:将元素插入在最后,然后调整一次堆,即可得到堆。 O(logN)
1 void Insert(int x){ 2 heap[++n]=x; 3 UpAdjust(1,n); 4 }
- 堆排序:一个堆只能保证第一个元素为最大/最小,并不能保证依次递增/递减,可以通过使用高效的堆排序算法,O(NlogN)。
算法思路:(假设下标从1-N)
- swap(heap[1],heap[N]) // 将最值换到最后
- DownAdjust(1,N-1); // 进行重新整理,那么这样N-1个元素重新形成堆,依次下去;
1 void HeapSort(){ 2 CreateHeap(); 3 for(int i=N;i>1;i++){ 4 swap(heap[1],heap[i]); 5 DownAdjust(1,N-1); 6 ) 7 }