堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法。学习堆排序前,先讲解下什么是数据结构中的二叉堆。
二叉堆的定义
:二叉堆是完全二叉树或者是近似完全二叉树。
二叉堆满足二个特性:
1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最大堆:
从图中可以看出
- 若父子结点下标为i,则左孩子的下标为i*2+1,右孩子的下标是i*2+2。
- 数组元素个数为size,则在下标size/2-1之后的结点都是叶子结点。
- 下标为size/2-1的结点是最后一个非叶子结点。
## 堆的插入删除 ##
这是《数据结构C++语言描述》中最小堆的建立插入删除的图解。
堆的插入
由图可得,每次插入时都是从最后一个位置插,相当于将一个元素插入到有序的数组中去,则插入后一直向上调整直到0位置。
代码如下
void HeapAdjustup(int a[],int i,int size) //向上调整
{
if(i > size)
return ;
int parent = (i-1)/2;
int min = parent;
if (parent <= size/2) //不是叶子结点才调整
{
if(a[i] < a[min])
min = i;
if (min == parent)
{
swap(a[min],a[parent]);
HeapAdjustup(a,parent,size);
}
}
}
void MinHeapFixup(int a[],int i) ///循环代码
{
for(int j = (i-1)/2 ; (j >0 && i != 0) && (a[i] > a[j]);i = j,j = (i-1)/2)
swap(a[i],a[j]);
}
void InsertHeap(int a[],int size,int key)
{
a[size] = key;
HeapAdjustup(a,size,size+1);
}
以下图为例分析小堆建立的过程,以及堆排序的原理
参考代码如下
void HeapAdjust(int a[],int i,int size)///向下调整
{
if (i >= size)
return ;
int left = 2*i+1; //左孩子的下标
int right = 2*i+2;//右孩子的下标
int min = i; //设置最小的下标为i
if (i<=size/2) //不是叶子结点的才调整
{
if (left < size&&a[left] < a[min]) //找到它的左孩子右孩子以及自己中最小的那个
min = left;
if (right < size&&a[right] < a[min])
min = right;
if (min != i) //如果最小的不是自己,就交换
{
swap(a[i],a[min]);
HeapAdjust(a,min,size); //递归调整以min为父节点的子树
}
}
}
void CreateHeap(int a[],int size)
{
for (int i = size/2-1;i>=0;--i)
HeapAdjust(a,i,size);
}
删除的代码
void HeapAdjust(int a[],int i,int size)///向下调整
{
if (i >= size)
return ;
int left = 2*i+1; //左孩子的下标
int right = 2*i+2;//右孩子的下标
int min = i; //设置最小的下标为i
if (i<=size/2) //不是叶子结点的才调整
{
if (left < size&&a[left] < a[min]) //找到它的左孩子右孩子以及自己中最小的那个
min = left;
if (right < size&&a[right] < a[min])
min = right;
if (min != i) //如果最小的不是自己,就交换
{
swap(a[i],a[min]);
HeapAdjust(a,min,size); //递归调整以min为父节点的子树
}
}
}
void DeleteHeap(int a[],int size)
{
swap(a[0],a[size-1]);
HeapAdjust(a,0,size-1);
}
至此,堆排序的原理以及实现代码已经写完。