数据结构—堆

堆的定义:

  1. 堆是一个完全二叉树 
  2. 堆有两种, 一种叫小堆(小根堆, 最小堆), 一种叫大堆(大根堆, 最大堆). 
  3. 以小堆为例, 这个树的根节点是这个树中的最小的元素,对于任意一个子树来说, 子树的根节点, 小于左右孩子节点的值. 
  4. 以大堆为例, 这个树的根节点是这个树种的最大元素,对于任意一个子树来说, 子树的根节点, 大于左右孩子节点的值. 

大堆结构:



小堆结构:


用小堆来举例:


对应上图,对堆进行插入分为两步;

  1. 将要插入如的元素放到数组的最后
  2. 不断的和他的父节点进行比较,如果比父节点小,则两个节点交换内容,知道父节点的值小于插入节点的值,这和个过程称为上浮


对堆进行删除:


同样分成两部分:

  1. 把树顶的元素和他的孩子节点中最小的交换位置,一直吧这个根节点交换到树的最后
  2. 堆的size--

堆排序:

堆排序就是把一个数组中的所有元素都一一插入一个堆,然后逐个删除,删除所有的元素之后,这是所有的元素已经有序,就打到了排序的目的

  • 从大到小排序需要一个小堆
  • 从小到大排序需要一个大堆

头文件 heap.h

#pragma once 
#include<unistd.h>
#include<stdio.h>
#define HeapMaxSize 1000   //堆结构体中数组的最大容量

typedef int HeapType;    //定义堆元素的类型
typedef int (*Compare)(HeapType* a, HeapType* b);  //返回值为int  参数为两个对类型指针的函数指针类型

typedef struct Heap {
  HeapType data[HeapMaxSize];
  size_t size;    //对中已有的元素个数    
  Compare cmp;   //由这个函数指针决定这个堆是大堆还是小堆
}Heap;
//这里的堆由一个数组来代替,父子节点之间的关系由数组下标之间的关系来维持
//比如一个几点的下标为x, 那么他的做孩子节点的下标就是2*x+1,右孩子节点的小标是2*x+2,父节点的下标是(x-1)/2
//这样做的好处就是省去了构建真正的树的麻烦操作
void HeapInit(Heap* heap, Compare compare); // 堆的初始化 void HeapInsert(Heap* heap, HeapType value); //向堆中插入元素 int HeapRoot(Heap* heap, HeapType* value); //取堆顶元素 void HeapErase(Heap* heap); // 删除堆顶元素 int HeapEmpty(Heap* heap); //判断堆书否为空 size_t HeapSize(Heap* heap); //获得堆的size void HeapDestroy(Heap* heap); //删除堆 // 在我们不想开辟额外的空间, 或者消耗额外的时间的前提下, // 如果我们想进行从小到大排序, 就需要一个大堆 // 如果我们想进行从大到小排序, 就需要一个小堆 void HeapSort(HeapType array[], size_t size);

头文件的具体实现 heap.c

#include "heap.h"

int MaxHeapCmp(HeapType* a, HeapType* b) {
  if(*a > *b) {
    return 1;
  } else {
    return 0;
  }

}

int MinHeapCmp(HeapType* a, HeapType* b) {
  if(*a < *b) {
    return 1;
  } else {
    return 0;
  }
}

void Swap(HeapType* a , HeapType* b) {
  HeapType x = *a;
  *a = *b;
  *b = x;
}

void HeapInit(Heap* heap, Compare compare) {
  if(heap == NULL) {
    //非法输入
    return;
  } 
  heap->size = 0;
  heap->cmp = compare;
}

void HeapInsert(Heap* heap, HeapType value) {
  if(heap == NULL) {
    //非法输入
    return;
  }
  if(heap->size >= HeapMaxSize) {
    //堆满
    return;
  }
  heap->data[heap->size++] = value;
  int cur = heap->size - 1;
  while(1) {
    if(!heap->cmp(&heap->data[cur],&heap->data[(cur -1)/2])){
      break;
    }
    if(heap->cmp(&heap->data[cur], &heap->data[(cur-1)/2])) {
      Swap(&heap->data[cur], &heap->data[(cur-1)/2]);
      cur = (cur - 1) / 2;
    }
  }
}

int HeapRoop(Heap* heap, HeapType* value) {
  if(heap == NULL) {
    return 0;
  }
  if(heap->size == 0) {
    return 0;
  }
  *value = heap->data[0];
  return 1;
}

void heapDown(Heap* heap) {
  size_t cur = 0; //标记当前要下沉的结点
  size_t lchild = 2*cur + 1;
  size_t rchild = 2*cur + 2;
  heap->size--;
  while(1){
    if(lchild < heap->size && rchild < heap->size) {
      //两个子树都在树的范围之内,选择最小的一个交换
      int exc;
      //exc用来保存满足条件的子节点的坐标
      //当这个堆是一个小堆时,exc保存左右子节点较小值的下标
      //当这个堆是一个大堆是,exc保存左右子节点较大值的下标
      exc = heap->cmp(&heap->data[lchild], &heap->data[rchild]) ? lchild : rchild;
      Swap(&heap->data[cur], &heap->data[exc]);
      cur = exc;
      lchild = 2*cur + 1;
      rchild = 2*cur + 2;
    } else if(lchild < heap->size) {
      //如果走到这里说明右子树超出树的范围,判断左子树是否满足条件
      // a) 满足交换
      // b) 不满足退出循环
      if(heap->cmp(&heap->data[lchild],&heap->data[cur])) {
        Swap(&heap->data[cur], &heap->data[lchild]);
      } 
      // 这个cur结点的特点就是只有左子树,不管交不交换,最后都直接退出程序
      return;
    } else {
      //如果走到这里说明左右子树不在树范围之内,说明已经到了树的最低层
      //直接返回
      return;
    }
  }
}

void HeapErase(Heap* heap) {
  if(heap == NULL) {
    return;
  }
  if(heap->size == 0) {
    return;
  }
  
  Swap(&heap->data[0], &heap->data[heap->size - 1]);
  heapDown(heap);
}

int HeapEmpty(Heap* heap) {
  if(heap == NULL) {
    return 0;
  }
  if(heap->size == 0) {
    return 0;
  } else {
    return 1;
  }
}

size_t HeapSize(Heap* heap) {
  if(heap == NULL) {
    return 0;
  }
  return heap->size; 
}

void HeapDestroy(Heap* heap) {
  if(heap == NULL) {
    return;
  }
  heap->size = 0;
  heap->cmp = NULL;
}

void HeapSort(HeapType array[], size_t size) {
  if(array == NULL) {
    return;
  }
  if(size == 0) {
    return;
  }
  Heap heap;
  //这里的初始化很关键
  //如果要从小到大排序,就需要一个大堆
  //若果要从大到小排序,就需要一个小堆
  HeapInit(&heap, MinHeapCmp); //这里演示小堆,即从大到小排序
  size_t i = 0;
  for(; i<size; i++) {
    HeapInsert(&heap, array[i]);
  }//循环完了之后i的值就是将数字插入完成之后堆的size的值
  
  while(heap.size > 0) {
    HeapErase(&heap);
  }
  //删除完毕之后,此时heap中的元素[0, i) 已经有序 
  //复制给array即可
  size_t j = 0;
  for(; j<i; j++) {
    array[j] = heap.data[j];
  }
}

# if 1
///////////////////////////////////////////////////////////////
// 以下为测试代码
///////////////////////////////////////////////////////////////

#define FUNHEAD printf("\n==============================%s=========================\n", __FUNCTION__)
void Print(Heap* heap) {
  if(heap == NULL) {
    return;
  }
  size_t i;
  for(i=0; i<heap->size; i++) {
    printf("%d ",heap->data[i]);
  }
  printf("\n");
}


void TestInit() {
  FUNHEAD;
  Heap heap;
  HeapInit(&heap, NULL);
  printf("heap.size expected 0, actual %lu\n", heap.size);
  printf("heap.cmp expected NULL, actauall %p\n", heap.cmp);
}

void TestInsert() {
  FUNHEAD;
  Heap heap;
  HeapInit(&heap, MinHeapCmp);
  HeapInsert(&heap, 9);
  HeapInsert(&heap, 8);
  HeapInsert(&heap, 7);
  HeapInsert(&heap, 6);
  HeapInsert(&heap, 5);
  HeapInsert(&heap, 4);
  HeapInsert(&heap, 3);
  Print(&heap);
}

void TestErase() {
  FUNHEAD;
  Heap heap;
  HeapInit(&heap, MinHeapCmp);
  HeapInsert(&heap, 9);
  HeapInsert(&heap, 8);
  HeapInsert(&heap, 7);
  HeapInsert(&heap, 6);
  HeapInsert(&heap, 5);
  HeapInsert(&heap, 4);
  HeapInsert(&heap, 3);
  size_t i = heap.size;
  while(heap.size != 0) {
    HeapErase(&heap);
    Print(&heap);
  }

  size_t j = 0;
  for(; j<i; j++) {
    printf("%d ",heap.data[j]);
  }
  printf("\n");
}

void TestSort() {
  FUNHEAD;
  int array[] = {1,6,9,4,8,5,77,34,76,22,1,6,0};
  size_t size = sizeof(array) / sizeof(array[0]);
  HeapSort(array, size);
  size_t i = 0;
  for(; i<size; i++) {
    printf("%d ",array[i]);
  }
  printf("\n");
}

int main() {
  TestInit();
  TestInsert();
  TestErase();
  TestSort();
}
#endif

实验结果:


void HeapInit(Heap* heap, Compare compare);  // 堆的初始化 

猜你喜欢

转载自blog.csdn.net/luhaowei0066/article/details/80557396