还不是很完整,后面会陆续补充
堆的定义:
n个关键字序列L[0...n-1]称为堆,当且仅当该序列满足:
- L(i) <= L(2i + 1) 且 L(i) <= L(2i + 2) ,此为最小堆。
- L(i) >= L(2i + 1) 且 L(i) >= L(2i + 2) ,此为最大堆。
算法步骤:
- 利用给定数组创建一个堆H[0...n-1](假定以最大堆为例),输出堆顶元素。
- 将最后一个(堆低)元素送入堆顶,此时根节点不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,然后再输出堆顶元素。
- 如此重复,直到堆中仅剩一个元素为止。
调整最小堆:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;
void AdjustMinHeap(vector<int>& data, int pos, int right)
{
int temp = data[pos];
while(2 * pos + 1 <= right)
{
int child = 2 * pos + 1; // child 为需要调整的元素(下标为 pos )的左子节点的下标位置
if(child + 1 <= right && data[child] > data[child +1] ) // 判断pos 是否存在右子节点,如果存在,则找出左子节点和右子节点的较小者
{
child = child + 1; // 右子节点比左子节点小,此时的位置更新为pos的右子节点
}
if(data[child] < temp)
{
data[pos] = data[child]; // 如果左右节点中较小的一个节点小于 pos 节点,则进行调整,也就是将这个较小的子节点赋值给父节点,
}
else
break; // 不需要调整,已经满足最小堆的性质了,所以跳出循环,
pos = child; //如果上述条件满足,还需要依次往下判断调整。将调整的位置更新为从 child 开始
}
data[pos] = temp; //将最开始位置的节点赋值给最后不需要调整的节点。因为一开始调整的时候,父节点是被左右子节点中较小的一个节点直接覆盖的,
// 然后子节点满足调整的条件的话,子节点又会被其左右子节点中较小的一个节点直接覆盖的。
}
void HeapSort(vector<int>& data)
{
int len = data.size();
for(int i = len/2 -1; i>=0; i--)//这里给定的关键字序列下标是从 0 开始计数的,所以从 (len/2-1)开始调整堆,即从最后一个元素的父节点开始。
{
AdjustMinHeap(data, i, len-1); //从最后一个元素的父节点开始建堆。
}
for(int i= len-1; i>=0; i--)
{
swap(data[i], data[0]); // 交换堆顶最小元素和序列的最后一个元素的位置。
AdjustMinHeap(data, 0, i-1); // 这里从位置0处开始向下调整是因为,上一步刚刚交换了堆顶元素和最后一个元素,
// 只是更换了堆顶的元素,所以只要向下调整堆顶元素到合适的位置就好了。
}
}
int main()
{
int numbers[] = {53, 17, 78, 9, 45, 65, 87, 32};
vector<int>num(numbers,numbers+8);
cout<<"数组的原来序列为:";
vector<int>::iterator it = num.begin();
for(it = num.begin(); it != num.end(); it++)
cout<< *it <<' ';
cout<< endl<<endl;
// 堆排序
HeapSort(num);
cout <<"经过最小堆的调整后,(每次将最小值放在序列的最后),数组变为: ";
for(it = num.begin(); it != num.end(); it++)
cout<< *it <<' ';
cout<< endl;
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/9a25ad3bbcefb47f12245f1f55119b36.png)
调整最大堆:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;
void AdjustMaxHeap(vector<int>& data, int pos, int right)
{
int temp = data[pos];
while(2 * pos + 1 <= right)
{
int child = 2 * pos + 1; // child 为需要调整的元素(下标为 pos )的左子节点的下标位置
if(child + 1 <= right && data[child] < data[child +1] ) // 判断pos 是否存在右子节点,如果存在,则找出左子节点和右子节点的较大者
{
child = child + 1; // 右子节点比左子节点大,此时的位置更新为pos的右子节点
}
if(data[child] > temp)
{
data[pos] = data[child]; // 如果左右节点中较大的一个节点大于 pos 节点,则进行调整,也就是将这个较大的子节点赋值给父节点,
}
else
break; // 不需要调整,已经满足最大堆的性质了,所以跳出循环,
pos = child; //如果上述条件满足,还需要依次往下判断调整。将调整的位置更新为从 child 开始
}
data[pos] = temp; //将最开始位置的节点赋值给最后不需要调整的节点。因为一开始调整的时候,父节点是被左右子节点中较大的一个节点直接覆盖的,
// 然后子节点满足调整的条件的话,子节点又会被其左右子节点中较大的一个节点直接覆盖的。
}
void HeapSort(vector<int>& data)
{
int len = data.size();
for(int i = len/2 -1; i>=0; i--)//这里给定的关键字序列下标是从 0 开始计数的,所以从 (len/2-1)开始调整堆,即从最后一个元素的父节点开始。
{
AdjustMaxHeap(data, i, len-1); //从最后一个元素的父节点开始建堆。
}
for(int i= len-1; i>=0; i--)
{
swap(data[i], data[0]); // 交换堆顶最小元素和序列的最后一个元素的位置。
AdjustMaxHeap(data, 0, i-1); // 这里从位置0处开始向下调整是因为,上一步刚刚交换了堆顶元素和最后一个元素,
// 只是更换了堆顶的元素,所以只要向下调整堆顶元素到合适的位置就好了。
}
}
int main()
{
int numbers[] = {53, 17, 78, 9, 45, 65, 87, 32};
vector<int>num(numbers,numbers+8);
cout<<"数组的原来序列为:";
vector<int>::iterator it = num.begin();
for(it = num.begin(); it != num.end(); it++)
cout<< *it <<' ';
cout<< endl<<endl;
// 堆排序
HeapSort(num);
cout <<"经过最大堆的调整后,(每次将最大值放在序列的最后),数组变为: ";
for(it = num.begin(); it != num.end(); it++)
cout<< *it <<' ';
cout<< endl;
return 0;
}
借助C++ STL库的堆实现:
1,使用heap相关的算法:
make_heap(): 根据指定的迭代器区间以及一个可选的比较函数,来创建一个heap. 时间复杂度O(N)
push_heap(): 把指定区间的最后一个元素插入到heap中. 时间复杂度O(logN)
pop_heap(): 弹出heap顶元素, 将其放置于区间末尾. 时间复杂度O(logN)
sort_heap():堆排序算法,通常通过反复调用pop_heap()来实现. 时间复杂度
N*O(logN)
C++11 加入两个新成员:
is_heap
: 判断给定区间是否是一个heap. 时间复杂度N
O(N)is_heap_until
: 找出区间中第一个不满足heap条件的位置. 时间复杂度N
O(N)
2,使用优先队列(priority_queue):