文章目录
堆(heap)
什么是堆
-
定义
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。
-
性质
- 结构性:用数组表示的完全二叉树。
- 有序性:任意节点的关键字(权值)是其子树所有节点的最大值(或最小值)
- 父节点大于子节点:最大堆(MaxHeap)
- 父节点小于子节点:最小堆(MinHeap)
-
应用
- 优先队列
“优先队列” (Priority Queue)是特殊的“队列”,从队列中取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
- 堆排序
- 优先队列
-
举例
- 堆:最大堆或最小堆
- 不是堆:不是完全二叉树或节点权值不符合要求
- 堆:最大堆或最小堆
最小堆的操作
- 数据名称:最小堆(MinHeap)
- 数据对象集:
一个有 个元素的最小堆是一棵完全二叉树,每个节点上的元素值不大于其子节点元素的值。 - 操作集:
对于任意最多有MaxSize个元素的最小堆 元素 ,主要操作有:- MinHeap Create(int MaxSize): 创建一个空的最小堆。
- Boolean IsFull(MinHeap H): 判断最小堆是否已满。
- Boolean IsEmpty(MinHeap H): 判断最小堆是否为空。
- Insert(MinHeap H,ElementType item): 将元素item插入最小堆H。
- ElementType DeleteMin(MinHeap H): 返回最小堆H中最小元素(高优先级)。
操作集的实现(C语言)
-
数组表示的完全二叉树
数组下标为0的位置放一个比所有堆中元素都要小的元素(可以是ElementType的最小值),称为“哨兵”。从下标为1的位置开始存放堆中元素。因为是完全二叉树,所以父亲节点与其左右子节点下标满足一些关系。
例:
由子节点找父节点:父节点下标=子节点下标 / 2
由父节点找左子节点:左子节点下标=父节点下标 * 2
由父节点找右子节点:右子节点下标=父节点下标 * 2 + 1 -
存储结构
typedef struct HeapStruct{ ElementType *Elements; /*存储堆元素的数组*/ int Size; /*堆的当前元素个数(最后一个元素的下标)*/ int Capacity; /*堆的最大容量*/ }HeapStruct,*MinHeap;
-
操作集的实现
-
MinHeap Create(int MaxSize): 创建一个空的最小堆
MinHeap CreateEmpty(int MaxSize) { MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct));//分配堆结构空间 H->Elenment = (int *)malloc((MaxSize + 1) * sizeof(int));//分配储存堆元素的数组的空间 H->Size = 0; H->Capacity = MaxSize; H->Elenment[0] = INT_MIN;//数组第一个元数放最小元素,数组第二个元素放堆中最小元素 return H; }
-
void Destroy(MinHeap H): 释放堆申请的空间
void Destroy(MinHeap H) { free(H->Elenment);//先释放堆节点的数组空间 free(H);//再释放堆节点的空间 }
-
Boolean IsFull(MinHeap H): 判断最小堆是否已满
int IsFull(MinHeap H) { return !(H->Capacity-H->Size);//判断最大容量Capacity是否小于等于堆中元素个数Size。 }
-
Boolean IsEmpty(MinHeap H): 判断最小堆是否为空
int IsEmpty(MinHeap H) { return !(H->Size - 0);//判断堆中元素个数是否等于0 }
-
Insert(MinHeap H,ElementType item): 将元素item插入最小堆H
//将元素item插入堆H void Insert(MinHeap H, int item) { int i = 0; //判断堆H是否已满 if (IsFull(H)) { printf("Heap is full\n"); return; } /* 如果H未满,将item放入堆最后一个元素,查看它的父节点,如果它的父节点比它大,将它和它的父节点互换位置, 循环此过程,直至它的父节点小于它。可能它比所有它的父节点都要小,但是一定会比哨兵大(数组中下标为0的位置), 所以一定最后它的下标一定大于哨兵的下标0。这就是哨兵的意义。 */ H->Size++; for (i = H->Size; H->Elenment[i / 2] > item; i = i / 2) { H->Elenment[i] = H->Elenment[i / 2]; } H->Elenment[i] = item; }
-
ElementType DeleteMin(MinHeap H): 返回最小堆H中最小元素(高优先级)
//将堆根节点元素取出,并将堆元素重新排序 int Delete(MinHeap H) { int item = 0,parent=0,child=0,temp; //判断堆是否已经空了 if (IsEmpty(H)) { printf("Heap is Empty\n"); return INT_MIN; } /* 堆没有空,将根节点返回,最后一个叶子节点放到根节点位置,然后比较它与它的左右子节点中最小 节点的大小,如果它比较大,则将它和它的较小的子节点互换位置,重复此过程,直至他比两个子节点 都小或者它不在有子节点 */ item = H->Elenment[1]; temp = H->Elenment[H->Size]; H->Size--; for (parent = 1; parent *2 <= H->Size; parent = child) { child = parent *2; //找出左右子节点最小的那个 if (child != H->Size && (H->Elenment[child] > H->Elenment[child + 1])) { child++; } //比较它与最小的子节点的大小,如果它比较大,则两者互换位置,否则跳出循环 if (temp > H->Elenment[child]) { H->Elenment[parent] = H->Elenment[child]; } else { break; } } H->Elenment[parent] = temp; return item; }
-
MinHeap CreateNotEmpty(int MaxSize,ElementType *Elenment, int Size): 创建一个非空的堆
已知堆中元素,但是不知道其在数组中的排列顺序,可以有两种创建堆的方法:一是先创建一个空堆,在将元素一个个插入堆中。二是直接将数组复制到堆节点的Element数组中,然后进行排序。
第二种方法比第一种方法时间复杂度更低。
MinHeap CreateNotEmpty(int MaxSize,int *Elenment, int Size) { int i = 0, temp = 0,parent=1,child=0; //创建一个空堆 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct));//分配堆结构空间 H->Elenment = (int *)malloc((MaxSize + 1) * sizeof(int));//分配储存堆元素的数组的空间 H->Size = 0; H->Capacity = MaxSize; H->Elenment[0] = INT_MIN;//数组第一个元数放最小元素,数组第二个元素放堆中最小元素 //将数组中元素移动到堆中 for (i = 0; i <Size; i++) { H->Elenment[i + 1] = Elenment[i]; } H->Size = Size ; //堆中元素排序 /* 最后一个节点的父节点的左右子节点都是一个堆,将最后一个节点的父节点和它的两个子节点排序(方法类似 与删除节点的操作),使得最后一个节点、其父节点和其兄弟节点形成一个堆。循环操作,从最后一个节点的 父节点往上依次执行这个操作,最后使得整个树都是一个堆。 */ for (parent = H->Size/2; parent>=1; parent--) { temp = H->Elenment[parent]; for (; parent * 2 <= H->Size; parent = child) { child = parent * 2; if (child != H->Size && (H->Elenment[child] > H->Elenment[child + 1])) { child++; } if (temp > H->Elenment[child]) { H->Elenment[parent] = H->Elenment[child]; } else { break; } } H->Elenment[parent] = temp; } return H; }
-