堆(heap)

 1、堆(heap)
堆数据结构是一种数组对象(也可以是链表),可以被看作一棵完全二叉树(二叉堆)。
堆总是满足下列性质:
1)堆中某个节点的值总是不大于或不小于其父节点的值;(在最大堆(max-heap)中,父节点的值不小于每一个子节点的值。在最小堆(min-heap)中,父节点的值不大于每一个子节点的值。)
2)堆总是一棵完全二叉树。
2、操作
获取最大(最小)元素
删除最大(最小)元素:下沉 
插入:上浮
堆化(heapify):Top-down reheapify (sink) 或 Bottom-up reheapify (swim)
删除或插入元素后要保持堆的性质,进行堆化。
3、应用
1)堆排序(heapsort)
堆排序(heapsort),像合并排序而不像插入排序,堆排序的运行时间为O(nlgn);像插入排序而不像合并排序,它是一种原地(in place)排序算法(在任何时候,数组中只有常数个元素存储在输入数组以外)。
2)优先级队列(priority queue)
作业调度与事件驱动(job scheduling or event-driven simulation)
具体实现可参考:libuv & libevent

关于堆排序
堆排序是所知的唯一能够同时最优的利用空间和时间的方法——在最坏的情况下也能保证2NlgN次比较和恒定的额外空间。现代系统许多应用很少使用它,因为它无法利用缓存。数组元素很少和相邻的其他元素进行比较,因此缓存未命中的次数要远高于大多数都在相邻元素间进行比较的算法。
关于优先队列
可以用无序或有序的数组或链表实现。使用无序是一种惰性(lazy)方法,在必要时才采取行动;使用有序是一种积极(eager)方法,尽可能的未雨绸缪,使后续操作更高效。
使用二叉堆实现,可以在删除最小元素和插入操作之间取得平衡,获取更高的效率。

以下主要内容参考《算法导论》

The (binary) heap data structure is an array object that we can view as a nearly complete binary tree. Each node of the tree corresponds to an element of the array. The tree is completely filled on all levels except possibly the lowest, which is filled from the left up to a point. An array A that represents a heap is an object with two attributes:A:length, which (as usual) gives the number of elements in the array, and A:heap-size, which represents how many elements in the heap are stored within array A.
 There are two kinds of binary heaps: max-heaps and min-heaps.
 In a max-heap, the max-heap property is that for every node i other than the root,
A[PARENT(i)] >= A[i]
 A min-heap is organized in the opposite way; the min-heap property is that for every node i other than the root,
A[PARENT(i)] <=A[i]
 For the heapsort algorithm, we use max-heaps. Min-heaps commonly implement priority queues.
 Viewing a heap as a tree, we define the height of a node in a heap to be the number of edges on the longest simple downward path from the node to a leaf, and we define the height of the heap to be the height of its root.

(1)堆化,上升的方式Bottom-up reheapify (swim)

	// Bottom-up reheapify (swim)
	while (i > 0 && LESS(A[i]->key, A[PARENT(i)]->key)) {
		SwapHE(A, i, PARENT(i));
		i = PARENT(i);
	}

(2)堆化,下降的方式Top-down reheapify (sink)

	int min = 0;

	// Top-down reheapify (sink)
	while (LEFT(i) < n) {
		min = LEFT(i);
		if (RIGHT(i) < n && LESS(A[RIGHT(i)]->key, A[LEFT(i)]->key))
			min = RIGHT(i);

		if (LESS(A[i]->key, A[min]->key))
			break;

		SwapHE(A, i, min);
		i = min;
	}

(3)构建堆时,注意对元素的遍历

练习代码

#pragma once
#ifndef _MY_HEAP_H
#define _MY_HEAP_H
 
typedef struct heapelem {
	int key;
	/* other data */

} HeapElem;

typedef struct helem {
	int key;
	int index;
	/* other data */

} HElem;

typedef struct heap {
	HElem *phe;
	int length;
	int size;
} HEAP;

#endif
//==============================================
// 堆的实现 
//==============================================
#include <stdio.h>
#include <stdlib.h>
#include "myheap.h"

#define MINVAL    0x0
#define MAXVAL    0xffff
#define LEFT(i)   (2 * (i) + 1)
#define RIGHT(i)  (2 * (i) + 2)
#define PARENT(i) (((i) - 1) / 2)

inline void swap(int *A, int i, int j)
{
	int tmp = A[i];
	A[i] = A[j];
	A[j] = tmp;
}

inline void swapHE(HeapElem *A, int i, int j)
{
	HeapElem tmp = A[i];
	A[i] = A[j];
	A[j] = tmp;
}

//============================================
// 采用下降的方法(非递归)
//============================================
void MinHeapify(int *A, int n, int i)
{
	int min; // 对小的孩子进行标记

	for (int j = i; j <= n / 2 - 1; j = min) {
		min = 2 * j + 1; // left
		if (min + 1 < n && A[min + 1] < A[min])
			min += 1;    // right
		if (A[j] > A[min]) {
			swap(A, j, min);
		}
	}
}

//============================================
// 采用下降的方法(递归)
//============================================
void MinHeapify1(int *A, int n, int i)
{
	int l = LEFT(i);
	int r = RIGHT(i);
	int min = i;

	if (r < n && A[r] < A[l])
		min = r;
	else if (l < n)
		min = l;
	else
		min = i;

	if (A[min] < A[i]) {
		swap(A, i, min);
		MinHeapify(A, n, min);
	}
}

//=============================================
// 获取堆中最小值
//=============================================
int HeapMin(int *A)
{
	return A[0];
}

//=============================================
// 获取堆中最小值(从堆中去掉)
//=============================================
int HeapExtractMin(int *A, int n)
{
	if (n < 1) {
		printf("heap underflow\n");
		return MAXVAL; // 根据情况设置
	}

	int min = A[0];
	A[0] = A[n - 1]; // 将最后一个元素移到第一个元素的位置然后进行堆化
	MinHeapify(A, n - 1, 0);

	return min;
}


/****************************************************/
/*  大顶堆 
/****************************************************/

//====================================================
// 采用下降的方法
//====================================================
void HeapShiftDown(HeapElem *A, int n, int i)
{
	HeapElem tmp = A[i];
	int max = 0;

	// Top-down reheapify (sink)
	while (LEFT(i) < n) {
		max = LEFT(i);
		if (RIGHT(i) < n && A[RIGHT(i)].key > A[LEFT(i)].key)
			max = RIGHT(i);

		if (A[max].key < tmp.key)
			break;

		A[i] = A[max];
		i = max;
	}
	A[i] = tmp;
}

//====================================================
// 采用上升的方法
//======================== ===========================
void HeapShiftUp(HeapElem *A, int n, int i)
{
	HeapElem tmp = A[i];

	// Bottom-up reheapify (swim)
	while (i > 0 && A[PARENT(i)].key < tmp.key) {
		A[i] = A[PARENT(i)];
		i = PARENT(i);
	}
	A[i] = tmp;
}

//====================================================
// 堆化,采用上升的方法
//======================== ===========================
void HeapifyU(HeapElem *A, int n)
{
	for (int i = 1; i < n; i++) {
		HeapShiftUp(A, n, i);
	}
}

//====================================================
// 堆化,采用下降的方法
//======================== ===========================
void HeapifyD(HeapElem *A, int n)
{
	// 遍历父节点 
	for (int i = (n - 1) / 2; i >= 0; i--) {
		HeapShiftDown(A, n, i);
	}
}

//====================================================
// 采用上升的方法
//======================== ===========================
void HeapSort(HeapElem *A, int n)
{
	// 创建大顶堆,上升
	HeapifyU(A, n);

	// 创建大顶堆,下沉
    // HeapifyD(A, n);

	for (int i = n - 1; i > 0; i--) {
		swapHE(A, i, 0);
		HeapShiftDown(A, i, 0);
	}
}

void PrintHeap(HeapElem *A, int n)
{
	int num = 2;

	for (int i = 0; i < n; i++) {
		printf(" %3d", A[i].key);
		if ((i + 1) == num - 1) {
			printf("\n");
			num *= 2;
		}
	}

	printf("\n========================\n");
}

void PrintAarry(HeapElem *A, int n)
{
	for (int i = 0; i < n; i++) {
		printf(" %3d", A[i].key);
	}

	printf("\n");
}

int main()
{
	int a[] = { 13, 23, 21, 19, 43, 3, 8, 11, 15, 14, 17, 18 };
	int len = sizeof(a) / sizeof(a[0]);
	HeapElem U[12];
	HeapElem D[12];

	for (int i = 0; i < len; i++) {
		U[i].key = a[i];
		D[i].key = a[i];
	}

	printf("up===============\n");
	PrintHeap(U, len);
	HeapifyU(U, len);
	PrintHeap(U, len);

	PrintAarry(U, len);
	HeapSort(U, len);
	PrintAarry(U, len);

	printf("down==============\n");
	PrintHeap(D, len);
	HeapifyD(D, len);
	PrintHeap(D, len);

	PrintAarry(D, len);
	HeapSort(D, len);
	PrintAarry(D, len);


	system("pause");
	return 0;
}

实用代码 

/* heap.h */
#ifndef _HEAP_H
#define _HEAP_H

typedef struct heapelem {
	int key;
	/* other data */
} HElem;

typedef struct heap {
	HElem *phe;
	int length;
	int size;
} HEAP;

#define LEFT(i)   (((i) << 1) + 1)
#define RIGHT(i)  (((i) << 1) + 2)
#define PARENT(i) (((i) - 1) >> 1)

//=========================================================
// 交换两元素
//=========================================================
void swapHElem(HElem *A, int i, int j);

//=========================================================
// 堆化,采用下降的方法
//=========================================================
void HeapifyShiftDown(HElem *A, int n, int i);

//=========================================================
// 建堆,采用下降的方法 build heap
//=========================================================
void BuildHeapDown(HEAP *heap);

//=========================================================
// 堆化,采用上升的方法
//=========================================================
void HeapifyShiftUp(HElem *A, int n, int i);

//=========================================================
// 建堆,采用上升的方法 build heap
//=========================================================
void BuildHeapUp(HEAP *heap);

//=========================================================
// 初始化堆
//=========================================================
void HeapInit(HEAP *heap, int n);

//=========================================================
// 释放堆
//=========================================================
void HeapFree(HEAP *heap);

//=========================================================
// 打印堆
//=========================================================
void HeapPrint(HEAP *heap);

#endif

/* heap.c */
#include <stdio.h>
#include <stdlib.h>
#include "heap.h"

/*********************************************************/
/*  最大堆(max-heap)
/*********************************************************/

//=========================================================
// 交换两元素
//=========================================================
void swapHElem(HElem *A, int i, int j)
{
	HElem tmp = A[i];
	A[i] = A[j];
	A[j] = tmp;
}

//=========================================================
// 堆化,采用下降的方法
//=========================================================
void HeapifyShiftDown(HElem *A, int n, int i)
{
	HElem tmp = A[i];
	int child = 0;

	// Top-down reheapify (sink)
	while (LEFT(i) < n) {
		child = LEFT(i);
		if (RIGHT(i) < n && A[RIGHT(i)].key > A[LEFT(i)].key)
			child = RIGHT(i);

		if (A[child].key < tmp.key)
			break;

		A[i] = A[child];
		i = child;
	}

	A[i] = tmp;
}

//=========================================================
// 建堆,采用下降的方法 build heap
//=========================================================
void BuildHeapDown(HEAP *heap)
{
	int n = heap->size;

	// 遍历父节点 
	for (int i = (n - 1) / 2; i >= 0; i--) {
		HeapifyShiftDown(heap->phe, n, i);
	}
}

//=========================================================
// 堆化,采用上升的方法
//=========================================================
void HeapifyShiftUp(HElem *A, int n, int i)
{
	HElem tmp = A[i];

	// Bottom-up reheapify (swim)
	while (i > 0 && A[PARENT(i)].key < tmp.key) {
		A[i] = A[PARENT(i)];
		i = PARENT(i);
	}

	A[i] = tmp;
}

//=========================================================
// 建堆,采用上升的方法 build heap
//=========================================================
void BuildHeapUp(HEAP *heap)
{
	int n = heap->size;

	for (int i = 1; i < n; i++) {
		HeapifyShiftUp(heap->phe, n, i);
	}
}

//=========================================================
// 初始化堆
//=========================================================
void HeapInit(HEAP *heap, int n)
{
	if (heap == NULL) {
		return;
	}

	heap->phe = malloc(sizeof(HElem) * n);
	if (heap->phe == NULL) {
		printf("malloc error!\n");
		return;
	}

	heap->length = n;
	heap->size = 0;
}

//=========================================================
// 释放堆
//=========================================================
void HeapFree(HEAP *heap)
{
	if (heap == NULL) {
		return;
	}

	free(heap->phe);
	heap->phe = NULL;

	heap->length = 0;
	heap->size = 0;
}

//=========================================================
// 打印堆
//=========================================================
void HeapPrint(HEAP *heap)
{
	int len = 0;

	len = heap->size;
	for (int i = 0; i < len; i++) {
		printf(" %8d", heap->phe[i].key);
	}

	printf("\n");
}
/* pqueue.h */
#ifndef _PRIORITY_QUEUE_H
#define _PRIORITY_QUEUE_H

#include "heap.h"

#if 0
typedef struct heapelem {
	int key;
	/* other data */
} HElem;

typedef struct heap {
	HElem *phe;
	int length;
	int size;
} HEAP;
#endif

#define PQElem HElem
#define PQUEUE HEAP

//=========================================================
// 初始化优先级队列
//=========================================================
void PQueueInit(PQUEUE *pq, int n);

//=========================================================
// 释放优先级队列
//=========================================================
void PQueueFree(PQUEUE *pq);

//=========================================================
// 添加元素到优先级队列
//=========================================================
void PQueueInsert(PQUEUE *pq, PQElem e);

//=========================================================
// 判断优先级队列是否为空
//=========================================================
int PQueueIsEmpty(PQUEUE *pq);

//=========================================================
// 获取优先级队列最大元素
//=========================================================
PQElem PQueueMax(PQUEUE *pq);

//=========================================================
// 去掉优先级队列最大元素
//=========================================================
void PQueueExtractMax(PQUEUE *pq);

//=========================================================
// 获取元素索引
//=========================================================
int PQueueGetIndex(PQUEUE *pq, PQElem e);

//=========================================================
// 增加第i个元素的key
//=========================================================
void PQueueIncreaseKey(PQUEUE *pq, int i, int key);

//=========================================================
// 打优先级队列
//=========================================================
void PQueuePrint(PQUEUE *pq);

#endif
/* pqueue.c */
#include <stdio.h>
#include <stdlib.h>
#include "pqueue.h"

/****************************************************/
/*  优先级队列(最大堆实现)
/****************************************************/

//=========================================================
// 添加元素到优先级队列
//=========================================================
void PQueueInsert(PQUEUE *pq, PQElem e)
{
	/* 满规格, 可以动态扩充 */
	if (pq->size >= pq->length) {
		return;
	}

	pq->phe[pq->size] = e;
	pq->size++;
	HeapifyShiftUp(pq->phe, pq->size, pq->size - 1);
}

//=========================================================
// 判断优先级队列是否为空
//=========================================================
int PQueueIsEmpty(PQUEUE *pq)
{
	return (pq->size == 0);
}

//=========================================================
// 获取优先级队列最大元素
//=========================================================
PQElem PQueueMax(PQUEUE *pq)
{
	return pq->phe[0];
}

//=========================================================
// 去掉优先级队列最大元素
//=========================================================
void PQueueExtractMax(PQUEUE *pq)
{
	if (pq->size > 0) {
		pq->phe[0] = pq->phe[pq->size - 1];
		pq->size--;
		HeapifyShiftDown(pq->phe, pq->size, 0);
	}
}

//=========================================================
// 去掉优先级最大元素
//=========================================================
int PQueueGetIndex(PQUEUE *pq, PQElem e)
{
	int index = 0;

	for (; index < pq->size; index++) {
		if (e.key == pq->phe[index].key) {
			return index;
		}
	}

	return index;
}

//=========================================================
// 增加第i个元素的key
//=========================================================
void PQueueIncreaseKey(PQUEUE *pq, int i, int key) 
{
	if (i >= pq->size) {
		return;
	}

	if (key < pq->phe[i].key) {
		return;
	}

	pq->phe[i].key = key;
	HeapifyShiftUp(pq->phe, pq->size, i);
}

//=========================================================
// 初始化优先级队列
//=========================================================
void PQueueInit(PQUEUE *pq, int n)
{
	HeapInit(pq, n);
}

//=========================================================
// 释放化优先级队列
//=========================================================
void PQueueFree(PQUEUE *pq)
{
	HeapFree(pq);
}

//=========================================================
// 打印优先级队列
//=========================================================
void PQueuePrint(PQUEUE *pq)
{
	HeapPrint(pq);
}

/* heapsort.h */
#ifndef _HEAP_SORT_H
#define _HEAP_SORT_H

#include "heap.h"

#if 0
typedef struct heapelem {
	int key;
	/* other data */

} HeapElem;

typedef struct helem {
	int key;
	int index;
	/* other data */

} HElem;

typedef struct heap {
	HElem *phe;
	int length;
	int size;
} HEAP;

#endif

//=========================================================
// 堆排序
//=========================================================
void HeapSort(HEAP *heap);

#endif
/* heapsort.c */
#include <stdio.h>
#include <stdlib.h>
#include "heap.h"

//=========================================================
// 堆排序
//=========================================================
void HeapSort(HEAP *heap)
{
	int i = 0;

	// 创建大顶堆,上升
	BuildHeapUp(heap);

	for (i = heap->size - 1; i > 0; i--) {
		swapHElem(heap->phe, i, 0);
		HeapifyShiftDown(heap->phe, i, 0);
	}
}
#include <stdio.h>
#include <stdlib.h>

//#include "heap.h"
#include "pqueue.h"
#include "heapsort.h"

int main()
{
	int a[] = { 13, 23, 21, 19, 43, 3, 8, 11, 15, 14, 17, 18 };
	int len = sizeof(a) / sizeof(a[0]);
	HEAP heap;

	HeapInit(&heap, len);
	for (int i = 0; i < len; i++) {
		heap.phe[i].key = a[i];
		heap.size++;
	}

	printf("heap sort before: \n");
	HeapPrint(&heap);

	HeapSort(&heap);

	printf("heap sort after: \n");
	HeapPrint(&heap);

	HeapFree(&heap);

	system("pause");
	return 0;
}

#if 0
int main()
{
	int i = 0;
	int j = 0;
	int a[] = { 13, 23, 21, 19, 43, 3, 8, 11, 15, 14, 17, 18 };
	int len = sizeof(a) / sizeof(a[0]);
	PQUEUE pqueue;
	PQElem pqe;

	PQueueInit(&pqueue, len);

	for (i = 0; i < len; i++) {
		pqe.key = a[i];
		PQueueInsert(&pqueue, pqe);
		PQueuePrint(&pqueue);
	}

	/* increase key */
	PQueueIncreaseKey(&pqueue, 7, 37);
	PQueuePrint(&pqueue);

	for (i = 0; i < len; i++) {
		pqe = PQueueMax(&pqueue);
		printf("max: %d\n", pqe.key);
		PQueueExtractMax(&pqueue);
		PQueuePrint(&pqueue);
	}

	PQueueFree(&pqueue);

	system("pause");
	return 0;
}
#endif

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值