堆:它的物理结构可以看做是数组,而逻辑结构可以看做一颗完全二叉树.
堆有大堆和小堆之分:
- 大堆:根节点是当前所有元素中的最大值,而对于每一个子树,其子树也是一个大堆.
- 小堆:根节点是当前所有元素中的最小值,而对于每一个子树,其子树也是一个小堆.
堆的应用:
堆还可用来表示优先级队列.队列:即只允许队列的队首进行删除,而队尾进行插入.而优先级队列就是一组有序的队列.在堆中,进行插入和删除时都是在队尾进行的,比如堆的插入,先插入到尾端,再进行调整到合适的位置;而堆的删除指的是删除堆的堆顶元素,首先要与堆尾元素交换然后再删掉堆尾元素,其实删掉的也是堆首元素.那么这样就和队列的插入和删除不谋而合了.
堆的插入
数组元素为:9,5,2,7,3 |
假设现在的堆是小堆,插入元素的步骤:
- 将元素插入到二叉树的最后
- 将插入的元素和其父节点的元素比较,若小于父节点,就把父节点其交换.
- 一直循环,直到父节点的元素小于子节点的元素.
过程描述如下图:
插入的代码实现:
//函数指针,用来判断时大堆还是小堆
int Less(heap_type a,heap_type b)
{
return a < b;
}
int Greater(heap_type a,heap_type b)
{
return a > b;
}
//上浮
void AdjustUp(heap_type data[],int size,int index,Compare cmp)
{
//Compare是一个函数指针,用来判断是要排小堆还是大堆
if(index >= size)
{
return;
}
int parent = (index - 1)/2;
int child = index;
//若子节点为0,就说明此时已经是根节点了
while(child > 0)
{
//如果子节点小于父节点,就与父节点交换,必须保证父节点是小的节点.(小堆的要求)
if(cmp(data[child],data[parent]))
{
swap(&data[child],&data[parent]);
}
else
{
break;
}
child = parent;
parent = (child - 1)/2;
}
}
void InsertHeap(heap* h,heap_type value)
{
if(h == NULL)
{
return;
}
if(h->size >= MAX_SIZE)
{
//堆满
return;
}
h->data[h->size++] = value;
AdjustUp(h->data,h->size,h->size - 1,h->cmp);
}
堆的删除
堆的删除指的是堆顶元素的删除,
删除步骤:
- 将堆顶元素和最后一个元素进行交换
- 减减堆的元素个数
- 然后调整堆,将堆依旧调整为一个小堆
如下图所示:
在调整堆时,采用下浮的方法,先比较左右子树的节点的大小,如果根节点的值大于子树的最小节点,就交换该子树节点和根节点.然后将一直循环将节点下移.
调整的代码如下:
//下沉
void AdjustDown(heap_type data[],int size,int index,Compare cmp)
{
if(index >= size)
{
return;
}
int parent = index;
int child = parent * 2 + 1; //左子树
while(child < size)
{
if(cmp(data[child + 1],data[child]) && (child + 1) < size)
{
//如果左子树大于右子树,那么就让child指向右子树.child是指向较小的节点
child = child + 1;
}
if(cmp(data[child] ,data[parent]))
{
//如果父节点大于子节点,就让交换
swap(&data[parent],&data[child]);
}
else
{
break;
}
parent = child;
child = parent * 2 + 1;
}
}
//删除堆顶元素的步骤:先与最后一个元素交换再将最后一个元素删除.然后再将堆调整为符合条件的堆.
void EraseHeapTop(heap* h)
{
if(h == NULL || h->size == 0)
{
return;
}
swap(&h->data[0],&h->data[h->size - 1]);
--h->size;
AdjustDown(h->data,h->size,0,h->cmp);
}
堆排序
- 升序排序,需要使用大堆来排序.
步骤:
①将数组中的所有元素全部元素按照上浮的方法插入到堆中.
②然后将堆顶元素和堆尾元素交换.
③删掉堆尾元素,然后减减堆的元素个数.
④将堆中的元素进行调整,使其时一个大堆. - 降序排序,需要用小堆来排序.
void Swap(heap_type* a,heap_type* b)
{
heap_type tmp = *a;
*a = *b;
*b = tmp;
}
void heap_sort(heap_type arr[],int size)
{
if(size <= 1)
{
return;
}
int i = 0;
for(i = 0;i < size;++i)
{
AdjustUp(arr,i + 1,i,Less); //降序
//AdjustUp(arr,i + 1,i,Greater); //升序
}
int Size = size;
while(Size > 0)
{
Swap(&arr[0],&arr[Size - 1]);
--Size;
AdjustDown(arr,Size,0,Less); //降序
//AdjustDown(arr,Size,0,Greater); //升序
}
}
方法二:当然也可以利用下沉来创建堆,然后进行排序.
void heap_sort2(heap_type arr[],int size)
{
if(size <= 1)
{
return;
}
int i = (size - 1 - 1)/2;
for(;i > 0;--i)
{
AdjustDown(arr,size,i - 1,Less); //降序
//AdjustDown(arr,size,i - 1,Greater); //升序
}
//AdjustDown(arr,size,0,Greater);
AdjustDown(arr,size,0,Less);
int Size = size;
while(Size > 0)
{
Swap(&arr[0],&arr[Size - 1]);
--Size;
AdjustDown(arr,Size,0,Less); //降序
//AdjustDown(arr,Size,0,Greater); //升序
}
}
堆排序时间复杂度:
堆排序的时间复杂度为:O(nlogn).
空间复杂度:O(1);