堆排序
二叉堆的本质是完全二叉树
大顶堆:每个节点的值都大于他的子节点
小顶堆:每个节点的值都小于他的子节点
二叉堆的根节点就是他的堆顶
堆的存储结构
虽然堆的数据结构是树,但是它并不是以链式结构存储的,而是顺序存储(数组)
通过数组下标可以定位父亲节点或者孩子节点,堆中的最后一个父亲节点就是(数组长度-1)/2,已知父亲节点的下标是n,那么孩子节点的下标就是2n+1和2n+2
堆排序的时间复杂度是O(nlogn)
堆排序思路
1.从下往上,找父亲节点,先找到堆中的最后一个父亲节点(ArrLen-1)/2,然后对他和他的叶子节点进行排序,方法如下:
求出两个叶子节点下标,判断两个叶子节点是否在数组范围内,然后挨个比对;
首先用父节点跟左孩子对比,如果父节点的值小于左孩子,那么最大值的下标就变成左孩子的下标,否则最大值下标就是父节点下标;
用右节点的值和最大值下标对应的节点进行对比,如果右节点更大,最大下标就变为右节点的下标,否则不变;
判断:如果最大节点下标不是父节点下标,说明父节点和他的子节点需要重新排序,父节点要和他的左孩子或者右孩子交换顺序,通过最大节点下标将父子节点中的值对换;
此时,最大值下标就表示换过位的子节点的位置,递归调用,以这个子节点为新的父节点,进行排序,此为一次循环。
2.之前找到的是堆中的最后一给父节点,因此需要向前遍历,将堆中的所有父节点进行排序。所作的操作就是:从(ArrLen-1)/2开始,向前遍历,每次遍历都进行排序操作,直到最终排到堆顶。
堆排序代码
#include<iostream> using namespace std; void BuildHeap(int ArrLen,int* Array); void MaxHeapify(int ArrLen,int* Array,int Index); int main(void) { int* Array; int ArrLen; cin >> ArrLen; Array = new int[ArrLen]; for (int i = 0; i < ArrLen; ++i) cin >> Array[i]; BuildHeap(ArrLen, Array); for (int i = 0; i < ArrLen; ++i) cout << Array[i] << " "; system("pause"); return 0; } void BuildHeap(int ArrLen,int* Array) { // 从最后一个节点的父节点往上调整 for (int i = (ArrLen-1)/2; i >=0; --i) MaxHeapify(ArrLen,Array,i); } void MaxHeapify(int ArrLen,int* Array,int Index) { //cout << "Array Index" << Index << endl; int Left = Index * 2 + 1; int Right = Index * 2 + 2; int MaxIndex,Temp; if (Left < ArrLen&&Array[Index] < Array[Left]) { MaxIndex = Left; } else MaxIndex = Index; if (Right < ArrLen&&Array[MaxIndex] < Array[Right]) MaxIndex = Right; //cout << "Max Index" << MaxIndex << endl; if (MaxIndex != Index) { Temp = Array[Index]; Array[Index] = Array[MaxIndex]; Array[MaxIndex] = Temp; MaxHeapify(ArrLen, Array, MaxIndex); } }
拓展:
求数组中第K大的数
1.使用堆排序来做就是在一次堆排序中,将最大值排到堆顶,然后将堆顶与堆底调换位置(数组的第一个元素和最后一个元素交换位置)
2.再进行堆排序,不理最后一个数(就是冒出来的堆顶),堆排序的长度缩减为原来的ArrLen-1(这样最后一个元素就不参与堆排序了)
3.循环执行k次,即可得到第k大的数
#include<iostream> using namespace std; void BuildHeap(int ArrLen,int* Array); void MaxHeapify(int ArrLen,int* Array,int Index); int KLargest(int ArrLen, int* Array, int K); int main(void) { int* Array; int ArrLen; cin >> ArrLen; Array = new int[ArrLen]; for (int i = 0; i < ArrLen; ++i) cin >> Array[i]; BuildHeap(ArrLen, Array); /*for (int i = 0; i < ArrLen; ++i) cout << Array[i] << " ";*/ int K; cin >> K; cout << KLargest(ArrLen, Array, K); system("pause"); return 0; } int KLargest(int ArrLen, int* Array, int K) { int PopValue; for (int i = 0; i < K; ++i) { BuildHeap(ArrLen + 1 - K, Array); for (int j = 0; j < ArrLen; ++j) cout << Array[j] << " "; cout << endl; PopValue = Array[0]; int Temp; Temp = Array[ArrLen - i-1]; Array[ArrLen - i-1] = Array[0]; Array[0] = Temp; for (int j = 0; j < ArrLen; ++j) cout << Array[j] << " "; cout << endl; } return PopValue; } void BuildHeap(int ArrLen,int* Array) { // 从最后一个节点的父节点往上调整 for (int i = (ArrLen-1)/2; i >=0; --i) MaxHeapify(ArrLen,Array,i); } void MaxHeapify(int ArrLen,int* Array,int Index) { //cout << "Array Index" << Index << endl; int Left = Index * 2 + 1; int Right = Index * 2 + 2; int MaxIndex,Temp; if (Left < ArrLen&&Array[Index] < Array[Left]) { MaxIndex = Left; } else MaxIndex = Index; if (Right < ArrLen&&Array[MaxIndex] < Array[Right]) MaxIndex = Right; //cout << "Max Index" << MaxIndex << endl; if (MaxIndex != Index) { Temp = Array[Index]; Array[Index] = Array[MaxIndex]; Array[MaxIndex] = Temp; MaxHeapify(ArrLen, Array, MaxIndex); } }