堆排序
1.堆排序
1.概念:堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序属于选择排序中的一种,它的最坏,最好的时间复杂度都是O,也是属于不稳定排序。
2.理论:堆是具有以下性质的完全二叉树,每个节点的值都是大于或者等于其左右孩子节点的值,称为大堆顶;或者每个节点的值都小于或等于其左右孩子节点的值,称为小堆顶;
3.图示:

观察上述数组p[N],在逻辑上就是一个堆结构
4.公式:
大栈顶:p[i] >= p[2i + 1] && p[i] >= p[2i + 2];
小栈顶:p[i] <= p[2i + 1] && p[i] <= p[2i + 2];
5.基本思想: 堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
模拟大栈顶:
a.假定一个无序的序列结构

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

此时,我们就将一个无需序列构造成了一个大顶堆。
思路总结:
a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
例题:
输入一个长度为n的整数数列,从小到大输出前m小的数。
第一行包含整数n和m。第二行包含n个整数,表示整数数列。
输出: 共一行,包含m个整数,表示整数数列中前m小的数。
代码:
#include <iostream>
using namespace std;
const int N = 100010;
int h[N], n, size1, m;
//down
void down(int u)
{
int p = u;
if(u * 2 <= size1 && h[u * 2] < h[p]) p = u * 2;//判断是左儿子还是右儿子
if(u * 2 + 1 <= size1 && h[u * 2 + 1] < h[p]) p = u * 2 + 1;
if(p != u)//如果不是栈顶,则交换位置
{
swap(h[p] , h[u]);
down(p);
}
}
/*void up(int u)
{
while(u / 2 && h[u / 2] > h[u])
{
swap(h[u / 2], h[u]);
u /= 2;
}
}*/
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> h[i];
size1 = n;
for(int i = n / 2; i ; i--) down(i);
while(m --)
{
cout << h[1] << " ";
h[1] = h[size1];
size1 --;
down(1);
}
return 0;
}
文章大部分讲解内容转自:https://www.cnblogs.com/chengxiao/p/6129630.html
浙公网安备 33010602011771号