数据结构 ---- 堆的基本操作

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

堆的初始化:

堆是用数组来定义的,并且在初始化时,指明此堆是大堆还是小堆,这里用一个函数指针来定义;

堆的插入:

思路:与树的插入相同,直接创建结点,然后插入到数组的尾部,不同的是,堆在插入完成时,仍要保证符合堆的性质,即大堆或者小堆,所以需要进行调整,这里采用上浮式调整方法;

取堆顶元素:

思路:因为是数组,所以数组下标为0的元素即是对顶元素;

删除堆顶元素:

思路:让堆顶元素与最后一个元素进行交换,再size减一,此时将对顶元素已经删掉,但是堆已经被破坏,所以要对堆进行调整,这里采用下沉式调整方法;

堆的判空:

思路:判断size是否为0;

堆的销毁:

思路:将堆的大小设置为0,函数指针指向空即可;

堆排序:

思路:在我们不想开辟额外的空间, 或者消耗额外的时间的前提下,
// 如果我们想进行从小到大排序, 就需要一个大堆
// 如果我们想进行从大到小排序, 就需要一个小堆
大堆:当我们循环删除对顶元素时,最后将所有都删除完了,此时的循序就是升序
小堆:与大堆的做法和思想完全相同,这个思路重点理解;
具体代码实现如下:

   #pragma once                                                                                       
   #include <stddef.h>

   #define HeapMaxSize 1024
   typedef char HeapType;

   //如果a和b满足比较关系,返回1
   //如果不满足比较关系,就返回0
   //所谓的比较关系,对于小堆来说,就是a<b
  //对于大堆来说,a>b
  typedef int(*Compare)(HeapType a,HeapType b);

  typedef struct Heap{
      HeapType data[HeapMaxSize];
      size_t size;
      Compare cmp;
  }Heap;  

  //在对堆进行初始化时,决定堆是大堆还是小堆
  void HeapInit(Heap* heap,Compare cmp);

  //销毁
  void HeapDestory(Heap* heap);

  //往堆中插入元素
  void HeapInsert(Heap* heap,HeapType value);

  //取堆顶元素
  int HeapRoot(Heap* heap,HeapType* value);

  //删除堆顶元素
  void HeapErase(Heap* heap);

  //根据arry数组,构建一个堆,这个堆用heap来表示
  void HeapCreate(Heap* heap,HeapType arry[],size_t size);

  //实现堆排序                                                                                       
  void HeapSort(HeapType arryp[],size_t size);

heap.c

#include <stdio.h>                                                                                 
   #include <string.h>
   #include "heap.h"

   //小堆
   int Less(HeapType a,HeapType b){
       return a<b?1:0;
   }
   //大堆
  int Greater(HeapType a, HeapType b){
      return a>b?1:0;
  }


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


  void HeapDestory(Heap* heap){
      if(heap ==NULL){
          //非法输入
          return;
      }
      heap->size = 0;
      heap->cmp = NULL;
      return;
  }
  void Swap(HeapType* a,HeapType* b){
      HeapType tmp = *a;
      *a = *b;                                                                                       
      *b = tmp;
      return;
  }
  void AdjustUp(Heap* heap,size_t index){
      size_t child = index;
      size_t parent = (child - 1)/2;                                                                 
      while(child > 0){
          if(!heap->cmp(heap->data[parent],heap->data[child])){
              Swap(&heap->data[parent],&heap->data[child]);
          }else{
              //如果发现某一时刻child 与 parent 符合堆的比较要求
              //此时就可以停止上浮,因为上面的元素肯定也是满足堆的要求的
              break;
          }
          child = parent;
          parent = (child - 1)/2;
      }
      return;
  }
  void HeapInsert(Heap* heap,HeapType value){
      if(heap == NULL){
          //非法输入
        return;
      }
      if(heap->size >= HeapMaxSize){
          //堆满了
          return;
      }
      heap->data[heap->size++] = value;
      //对堆进行上浮式调整
      //调整的起始位置是size - 1
      AdjustUp(heap,heap->size - 1);
      return;
  }

  int HeapRoot(Heap* heap,HeapType* value){
      if(heap == NULL){
          //非法输入
          return 0;                                                                                  
      }
      if(heap->size == 0){
          //空堆
          return 0;
      }
      *value = heap->data[0];
      return 1;
  }

  void AdjustDown(Heap* heap,size_t index){
      if(heap == NULL){
          return;
      }
      size_t parent = index;
      size_t child = 2 * parent + 1;
      while(child < heap->size){
          if(child + 1 < heap->size && heap->cmp(heap->data[child+1],heap->data[child])){
              //如果右子树存在,并且右子树比左子树更符合“堆的要求”
              //假设我们是小堆,就要求说:
              //如果右子树比左子树小,那么就让child指向右子树
              child = child + 1;                                                                     
          }
          //child就指向了左右子树中更小的那个元素
         if(heap->cmp(heap->data[child],heap->data[parent])){
             Swap(&heap->data[parent],&heap->data[child]);
         }else{
             break;                                                                                 
         }
         parent = child;
         child = 2 * parent + 1;
     }
     return;
 }
 void HeapErase(Heap* heap){
     if(heap == NULL){
         //非法输入
         return;
     }
     if(heap->size == 0){
         //空堆
         return;
     }
     //交换堆顶元素与最后一个元素
     Swap(&heap->data[0],&heap->data[heap->size - 1]);
     //size--
     heap->size--;
     //调整堆
     //从根结点出发,进行下沉式调整
     AdjustDown(heap,0);
     return;
 }

 void HeapCreate(Heap* heap,HeapType arry[],size_t size){
     if(heap == NULL){                                                                              
         return;
     }
     //遍历arry数组,将数组中的元素插入到堆中
     size_t i = 0;
     for(;i<size;i++){
         HeapInsert(heap,arry[i]);
     }
     return;
 }
 //假设我们要进行升序排序,我们要建立一个大堆
 void HeapSort(HeapType arryp[],size_t size){
     //把这个数组构建成一个堆
     Heap heap;
     HeapInit(&heap,Greater);
     HeapCreate(&heap,arryp,size);
     //循环的对堆进行删除操作
     while(heap.size > 0){
         HeapErase(&heap);
     }
     //循环结束之后堆排序就完成了
     memcpy(arryp,heap.data,size*sizeof(HeapType));
     return;
 }

猜你喜欢

转载自blog.csdn.net/liru_1996/article/details/80389905