数据结构之堆
介绍
优先级队列,又叫堆,是一个完全二叉树,其中每个节点的值总是大于(小于)或者等于子节点的值,所以就有大顶堆和小顶堆。一般树的实现都是用结点连接而成,也即使用指针,但是因为堆是一个完全二叉树,所以使用数组来表示更方便,从数组下标0开始,位置为i的结点其父节点位置一定是(i-1)/2,而它两个子节点的位置一定是2i+1,2i+2。
堆的实现
插入元素
在堆中插入元素,一般是插入到数组的最后一个位置,然后开始上浮调整,插入元素之前默认堆已经建好了,我们以大顶堆为例子。
//插入元素
void inSert(vector<int>&arr,int data){
arr.push_back(data);//将数据插入到数组末尾,然后开始上浮调整
swim(arr,arr.size()-1);//然后开始上浮调整
}
//元素上浮调整,此时默认其他元素已经组成了大顶堆
void swim(vector<int>&7arr,int start){
int c=start;//当前结点所处的位置,这里是数组最后一个元素的位置
int p=(c-1)/2;//其父节点的位置
int tmp=arr[c];//当前节点的元素值,后面单向赋值使用
while(c>0){//从最后一个位置依次和其父节点比较,直到根节点
if(tmp<=arr[p])break;//如果插入的元素值已经小于等于其父节点的值,说明已经有序了,已经满足堆的性质了
else{//否则和父节点开始交换元素
arr[c]=arr[p];//单向赋值即可,移动的是要插入的值,对要插入的值做出改变即可
c=p;//索引改变,直接交换也可
p=(p-1)/2;//同时父节点更新
}
}
//将最终插入位置的值和要插入的元素的值交换
arr[c]=tmp;//
}
删除元素
删除元素一般是删除堆顶的元素,可以直接将堆顶元素删除,然后再调正堆满足自身性质。这里使用数组最后一个元素直接放到堆顶,然后删除最后一个位置上的值和空间,再下沉这个元素调整堆。
void reMove(vector<int>&arr,int data){
arr[0]=arr.back();//已经是大顶堆,然后将最后一个元素赋值给堆顶,再调整即可
arr.pop_back();//将最后一个元素从堆中剔除
sink(arr,0,arr.size()-1);//下沉调整
}
//
void sink(vector<int>&arr,int start,int end){
int c=start;//当前节点的位置
int l=2*c+1;//当前节点的左孩子节点的位置
int tmp=arr[c];//当前节点的数值
while(l<=end){//左闭右闭区间,
if(l+1<=end&&arr[l]<arr[l+1]){//左右孩子中找到最大的,交换最大的,因为是大顶堆
l++;//
}
if(tmp>=arr[l])break;//如果当前节点已经大于其左右孩子的最大值,就已经满足条件了
else{//这里是单向赋值,一直修改父节点,到退出循环再把当前节点赋值。也可以直接swap,
arr[c]=arr[l];//否则,左右孩子中最大的节点赋值给父节点,
c=l;//更新下标
l=2*l+1;//然后更新孩子节点,继续找下一个三角是否满足根节点最大
}
}
arr[c]=tmp;//最后找到的位置,将当前节点的值更新
}
堆的建立
//构建,给一个无序的完全二叉树,然后将其构建成大顶堆。数组形式。
//让所有非叶子节点下沉,不过是从非叶子节点下沉然后向上直到根节点,
void buildMaxHeap(vector<int>&arr,int heapSize){
for(int i=(heapSize+1)/2-1;i>=0;i--){//非叶子节点的计算,n/2-1.n是元素个数
sink(arr,i,heapSize);//左闭右闭
}
}
STL之优先队列
//堆STL使用,优先队列
//priority_queue,每次只能访问位于队头的元素。
//先进队列的不一定先出,而是优先级最大的元素先出队列。默认是大顶堆。
//STL自定义排序
struct cmp{
bool operator()(int a,int b){
return a>b;//>对应greater,升序排列,小顶堆
}
};
vector<int> stl_heapSort(vector<int>&arr){
priority_queue<int,vector<int>,cmp>q(arr.begin(),arr.end());//
//默认是大顶堆
priority_queue<int,vector<int>,less<int>>;
//也可改成小顶堆
priority_queue<int,vector<int>,greater<int>>;
int n=arr.size();
vector<int>result;
while(!q.empty()){//priority_queue没有迭代器,所以只能通过移除一个个元素来进行访问
result.push_back(q.top());//普通队列是q.front();
q.pop();
}//STL中堆会自己调整
//reverse(result.begin(),result.end());
return result;
}
堆排序
//堆排序,建立的大顶堆只是堆顶元素最大,
//依次将堆顶的元素移除放入数组,这里直接和最后一个元素交换就行,然后调整剩余的未排序的数组,之后重复交换堆顶和数组末尾元素
//其实是移除堆顶元素,数组长度减一的操作,这里原地操作了,也可以用辅助数组来存储堆顶元素
void heapSort(vector<int>&arr){
int n=arr.size();
buildMaxHeap(arr, n-1);//先构建堆,左闭右闭区间
for(int i=n-1;i>=1;i--){//依次交换堆顶和数组尾部元素,这是数组最后一个元素就是最大值,依次类推,然后调整未排序的数组
swap(arr[i],arr[0]);//交换
sink(arr,0,i-1);//交换的堆顶元素是排好序的,调整剩下的未排序元素即可。依次类推
}
}
经典题目:
参考:leetcode