堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。
堆一般使用优先队列(priority_queue)实现,而优先队列默认情况下使用的是大顶堆。
堆的两个特性:
1、结构性:用数组表示完全二叉树
2、有序性:任一结点的关键词是其子树所有结点的最大值(或最小值)
最大堆,也称“大顶堆”:父亲结点的值大于或等于结点的值
最小堆,也称“小顶堆”:父亲结点的值小于或等于孩子结点的值
注:从根节点到任意结点路径上结点序列的有序性!
一、堆的存储:
使用数组来存储完全二叉树,这样结点就按层序存储于数组中,其中第一个结点将存储于数组中的1号位,并且数组i号位表示的结点的左孩子就是2i号位,而右孩子则是(2i+1)号位。
二、堆的基本操作:
#include<bits/stdc++.h> using namespace std; #define inf 0x3fffffff const int maxn=1010; //heap为堆,n为元素个数 int heap[maxn],n=100; //向下调整 时间复杂度为O(logn) void downAjdust(int low,int high){ int i=low,j=i*2; //i为欲调整结点,j为其左孩子 while(j<=high){ //存在孩子结点 if(j+1<=high&&heap[j+1]>heap[j]){//如果存在右孩子结点,且右孩子的值大于左孩子 j=j+1; //让j存储右孩子的下标 } if(heap[j]>heap[i]){ //如果孩子中最大权值比欲调整结点i大 swap(heap[j],heap[i]); //交换最大权值的孩子与欲调整结点i i=j; //保持i为欲调整结点,j为i的左孩子 j=i*2; } else{ break; //孩子的权值均比欲调整结点i小,调整结束 } } } //建堆 时间复杂度O(n) void creatHeap(){ for(int i=n/2;i>=1;i--){//从最后一个非叶子结点开始枚举 downAdjust(i,n); } } //删除堆顶元素 时间复杂度O(logn) void deleteTop(){ heap[1]=heap[n--]; //用最后一个元素覆盖堆顶元素,并让元素个数减 1 downAdjust(1,n); //向下调整堆顶元素 } //向上调整 时间复杂度为O(logn) void upAdjust(int low,int high){//对heap数组在[low,high]范围进行向上调整,其中low一般设置为1 //high表示欲调整结点的数组下标 int i=high,j=i/2;//i为欲调整结点,j为其父亲 while(j>=low){ //父亲在[low,high] 范围内 if(heap[j]<heap[i]){ //父亲权值小于欲调整结点i的权值 swap(heap[j],heap[i]); //交换父亲和欲调整结点 i=j; //保持i为欲调整结点,j为i的父亲 j=i/2; } else{ break; //父亲权值比欲调整结点i的权值大,调整结束 } } } //添加元素 时间复杂度O(logn) void insert(int x){ heap[++n]=x; //让元素个数加 1 ,然后将数组末位赋值为 x upAdjust(1,n); //向上调整新加入的结点 n } int main(){ return 0; }
堆排序:
//堆排序 时间复杂度O(nlogn) void heapSort(){ creatHeap(); //建堆 for(int i=n;i>1;i--){ //倒着枚举,直到堆中只有一个元素 swap(heap[i],heap[1]); //交换heap[i]与堆顶 downAdjust(1,i-1); //向下调整堆顶 } }
堆排序完整代码:
#include<bits/stdc++.h> using namespace std; #define inf 0x3fffffff const int maxn=1010; //heap为堆,n为元素个数 int heap[maxn],n=100; //向下调整 时间复杂度为O(logn) void downAdjust(int low,int high){ int i=low,j=i*2; //i为欲调整结点,j为其左孩子 while(j<=high){ //存在孩子结点 if(j+1<=high&&heap[j+1]>heap[j]){//如果存在右孩子结点,且右孩子的值大于左孩子 j=j+1; //让j存储右孩子的下标 } if(heap[j]>heap[i]){ //如果孩子中最大权值比欲调整结点i大 swap(heap[j],heap[i]); //交换最大权值的孩子与欲调整结点i i=j; //保持i为欲调整结点,j为i的左孩子 j=i*2; } else{ break; //孩子的权值均比欲调整结点i小,调整结束 } } } //建堆 时间复杂度O(n) void creatHeap(){ for(int i=n/2;i>=1;i--){//从最后一个非叶子结点开始枚举 downAdjust(i,n); } } //删除堆顶元素 时间复杂度O(logn) void deleteTop(){ heap[1]=heap[n--]; //用最后一个元素覆盖堆顶元素,并让元素个数减 1 downAdjust(1,n); //向下调整堆顶元素 } //向上调整 时间复杂度为O(logn) void upAdjust(int low,int high){//对heap数组在[low,high]范围进行向上调整,其中low一般设置为1 //high表示欲调整结点的数组下标 int i=high,j=i/2;//i为欲调整结点,j为其父亲 while(j>=low){ //父亲在[low,high] 范围内 if(heap[j]<heap[i]){ //父亲权值小于欲调整结点i的权值 swap(heap[j],heap[i]); //交换父亲和欲调整结点 i=j; //保持i为欲调整结点,j为i的父亲 j=i/2; } else{ break; //父亲权值比欲调整结点i的权值大,调整结束 } } } //添加元素 时间复杂度O(logn) void insert(int x){ heap[++n]=x; //让元素个数加 1 ,然后将数组末位赋值为 x upAdjust(1,n); //向上调整新加入的结点 n } //堆排序 时间复杂度O(nlogn) void heapSort(){ creatHeap(); //建堆 for(int i=n;i>1;i--){ //倒着枚举,直到堆中只有一个元素 swap(heap[i],heap[1]); //交换heap[i]与堆顶 downAdjust(1,i-1); //向下调整堆顶 } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>heap[i]; } heapSort(); for(int i=1;i<=n;i++){ cout<<heap[i]<<" "; } cout<<endl; return 0; } //示例: //5 输入 //3 1 4 5 2 输入 //1 2 3 4 5 输出