堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。

堆一般使用优先队列(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     输出

 

posted @ 2021-04-03 23:04  XA科研  阅读(108)  评论(0编辑  收藏  举报