【数据结构】堆的基本操作

堆是一颗完全二叉树,所有元素是按照完全二叉树的顺序存储方式放在一个数组,堆中每个结点都比其孩子结点小(大),叫小(大)堆
所以,堆的底层是按数组方式来存储,数组元素可能有序,可能无序,但堆中元素是有顺序的,要么是小堆,要么是大堆。

下图是数组对应的完全二叉树,但还不是堆,所以在创建堆时,需要对数组进行向下调整,建成小堆或大堆
在这里插入图片描述

  • 建堆(这里我建的小堆,你也可以建大堆,后面会介绍如何一起来建大小堆)
    如果从第一个节点调整,不能保证向下调整后,堆顶元素最小,因此不能满足堆的特性,只能看出,双亲结点小于孩子节点,所以在调整时,需要从倒数第一个非叶子节点(size-1-1)/2开始,依次往上
void HeapAdjustDown(Heap* hp,int parent)
{
 int child = parent * 2 + 1;
 while (child < hp->size)//保证左孩子存在
 {
  if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保证右孩子存在,要不然咋比
   child = child + 1;
  if (hp->array[parent] > hp->array[child])
  {
   Swap(&hp->array[child], &hp->array[parent]);
   parent = child;
   child = parent * 2 + 1;
  }  
  else
   return; 
 }
}
void HeapCreate(Heap* hp, DataType arr[], int size)//创建
{
 assert(hp);
 hp->array = (DataType*)malloc(sizeof(DataType)*size);
 if (hp->array==NULL)//
 {
  assert(0);//看为什么失败
  return;
 }
 hp->capacity = size;
 memcpy(hp->array, arr, sizeof(DataType)*size);
 hp->size = size;
 //对堆中元素进行向下调整,重复调几次
 //调上面的左右子树之前,要从倒数第一个非叶子结点开始
 for (int root = (size - 2) >> 1; root >= 0;root--)
  HeapAdjustDown(hp, root);
  • 堆的插入与删除
    堆的插入是在数组末插入元素,所以插入后,需要向上调整,把该元素放到合适的位置
    删除是删数组第一个元素,也就是堆顶,需要1.堆顶与堆尾元素交换,2.向下调整元素至合适位置
void HeapPush(Heap* hp, DataType data)//插入
{
 assert(hp);
 CheckCapacity(hp);//检测还有无空间
 hp->array[hp->size] = data;
 hp->size++;
 HeapAdjustUp(hp,hp->size-1);
 }
void HeapPop(Heap* hp)//堆的删除
{
 assert(hp);
 if (HeapEmpty(hp)) return;
 Swap(&hp->array[0], &hp->array[hp->size - 1]);
 hp->size--;
 HeapAdjustDown(hp, 0);
}

Heap.h代码

#include "Heap.h"
void Swap(int* left, int* right)
{
 int tmp = *left;
 *left = *right;
 *right = tmp;
}
void CheckCapacity(Heap* hp)
{
 assert(hp);
 int newCapacity = hp->capacity * 2;
 if (hp->size >= hp->capacity)
 {
  //需要扩容
  DataType* temp = (DataType*)malloc(sizeof(DataType)*newCapacity);
  if (temp == NULL)
  {
   assert(0);
   return;
  }
  //memcpy(temp, hp->array, sizeof(DataType)*hp->size);
  for (int i = 0; i < hp->size; i++)
   temp[i] = hp->array[i];
  free(hp->array);
  hp->array = temp;
  hp->capacity = newCapacity;
 }
}
void HeapAdjustUp(Heap* hp, int child)
{
 int parent = (child - 1) >> 1;
 //不需要找孩子中最小的与双亲比较
 //插入之前已经满足堆的特性了
 while (child)
 {
  if (hp->array[child] < hp->array[parent])
  {
   Swap(&hp->array[child], &hp->array[parent]);
   child = parent;
   parent = (child - 1) >> 1;
  }
  else
   return;
 }
}
void HeapAdjustDown(Heap* hp,int parent)
{
 int child = parent * 2 + 1;
 while (child < hp->size)//保证左孩子存在
 {
  if (hp->array[child] > hp->array[child + 1] && child + 1< hp->size)//保证右孩子存在,要不然咋比
   child = child + 1;
  if (hp->array[parent] > hp->array[child])
  {
   Swap(&hp->array[child], &hp->array[parent]);
   parent = child;
   child = parent * 2 + 1;
  }  
  else
   return; 
 }
}
void HeapCreate(Heap* hp, DataType arr[], int size)//创建
{
 assert(hp);
 hp->array = (DataType*)malloc(sizeof(DataType)*size);
 if (hp->array==NULL)//
 {
  assert(0);//看为什么失败
  return;
 }
 hp->capacity = size;
 memcpy(hp->array, arr, sizeof(DataType)*size);
 hp->size = size;
 //对堆中元素进行向下调整,重复调几次
 //调上面的左右子树之前,要从倒数第一个非叶子结点开始
 for (int root = (size - 2) >> 1; root >= 0;root--)
  HeapAdjustDown(hp, root);
}
void HeapDestory(Heap* hp)//销毁
{
 assert(hp);
 if (hp->array)
 {
  free(hp->array);
  hp->array =NULL;
  hp->capacity = 0;
  hp->size = 0;
 }
}
void HeapPush(Heap* hp, DataType data)//插入
{
 assert(hp);
 CheckCapacity(hp);
 hp->array[hp->size] = data;
 hp->size++;
 HeapAdjustUp(hp,hp->size-1);
}
void HeapPop(Heap* hp)//堆的删除
{
 assert(hp);
 if (HeapEmpty(hp)) return;
 Swap(&hp->array[0], &hp->array[hp->size - 1]);
 hp->size--;
 HeapAdjustDown(hp, 0);
}
DataType HeapTop(Heap* hp)//取堆顶元素
{
 assert(hp&&!HeapEmpty(hp));
 return hp->array[0];
}
int HeapSize(Heap* hp)//堆的数据个数
{
 assert(hp);
 return hp->size;
}
int HeapEmpty(Heap* hp)//堆判空
{
 assert(hp);
 return hp->size == 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章