堆排序是面试中很重要的一个算法,虽然很多人之前可能有接触过,但或许已经忘了,包括我自己也是,所以今天就来做一下总结,简单易懂
下面先看一下堆排序的动态演示(一个超级好用的网站,各种算法相关的动态图都能演示)
https://www.cs.usfca.edu/~galles/visualization/HeapSort.html
(再附上一个动图,来源leetcode )
代码实现
#include<bits/stdc++.h>
using namespace std;
//以某个点为父节点构造大根堆
void bigHeapify(int parent, vector<int>& nums, int length)
{
int child = 2*parent+1; //父节点的第一个子节点
while(child<length)
{
if (child+1<length && nums[child]<nums[child+1]) //若该父节点有两个子节点,则child指向其中较大的一个
child++;
if (nums[parent] < nums[child]) //若父节点比较大的子节点小,则对他们进行交换
{
swap(nums[parent], nums[child]);
parent = child; //这里注意还要继续往下便利,确保下面的堆以然是一个大根堆
child = parent*2+1;
}
else //父节点比较大字节点大,说明已经是一个大根堆,可以结束
return;
}
}
//以某个点为父节点构造小根堆,原理同上
void smallHeapify(int parent, vector<int>& nums, int length)
{
int child = 2*parent+1;
while(child<length)
{
if (child+1<length && nums[child+1]<nums[child])
child++;
if (nums[child] < nums[parent])
{
swap(nums[parent], nums[child]);
parent = child;
child = parent*2+1;
}
else
return;
}
}
void initializeHeap(vector<int>& nums, int length)
{
for (int i=length/2-1; i>=0; i--)
{
//从最后一个非叶子结点开始往上构造大根堆,千万注意起始点和边界条件!
bigHeapify(i, nums, length);
}
}
void display(vector<int>& nums, int length)
{
cout<<"排序后的数组:";
for (int i=0; i<length; i++)
cout<<nums[i]<<" ";
cout<<endl;
}
void Heap(vector<int>& nums, int length)
{
for (int i=length-1; i>0; i--) //总共需要交换length-1次
{
swap(nums[0], nums[i]);
display(nums, length); //将交换一次后的数组先打印出来,方便观察
bigHeapify(0, nums, i); //交换完之后记得从0这个点构造大根堆
}
}
int main()
{
vector<int> nums = {49, 38, 65, 97, 76, 13, 27, 49, 10}; //待排序的数组
int length = nums.size(); //数组的长度
initializeHeap(nums, length); //初始化堆,在初始化堆中可以选择构造大根堆或者小根堆
Heap(nums, length); //调用堆排序算法
return 0;
}
总结 平均时间复杂度:O(n * log n)
空间复杂度:O(1)
稳定性:不稳定