【數據結構】堆的基本操作

堆是一顆完全二叉樹,所有元素是按照完全二叉樹的順序存儲方式放在一個數組,堆中每個結點都比其孩子結點小(大),叫小(大)堆
所以,堆的底層是按數組方式來存儲,數組元素可能有序,可能無序,但堆中元素是有順序的,要麼是小堆,要麼是大堆。

下圖是數組對應的完全二叉樹,但還不是堆,所以在創建堆時,需要對數組進行向下調整,建成小堆或大堆
在這裏插入圖片描述

  • 建堆(這裏我建的小堆,你也可以建大堆,後面會介紹如何一起來建大小堆)
    如果從第一個節點調整,不能保證向下調整後,堆頂元素最小,因此不能滿足堆的特性,只能看出,雙親結點小於孩子節點,所以在調整時,需要從倒數第一個非葉子節點(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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章