数据结构--堆( 创建 & 插入 & 删除 & 应用 )

 堆的基本概念 :  

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

  • 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

正常的数组存储 : 

              

改为堆存储后 : 大堆   arry[] = {9 , 17 , 65 , 23 , 45 , 78 , 87 , 53 , 31};

                         小堆   arry[] = {87 , 78 , 53 , 45 , 65 , 9 , 31 , 17 , 23};

             

小堆(大堆)中 :任一结点的关键码均小于(大于)等于它的左右孩子的关键码,位于堆顶结点的关键码最小(最大),

从根节点到每个结点的路径上数 组元素组成的序列都是递增(递减)的堆存储在下标为0开始的数组中,因此在

堆中给定下标为i的结点时 ;

  1. 如果i=0,结点i是根节点,没有双亲节点;否则结点i的双亲结点为 结点(i-1)/2
  2. 如果2 * i + 1 <= n - 1,则结点i的左孩子为结点2 * i + 1,否则结 点i无左孩子
  3. 如果2 * i + 2 <= n - 1,则结点i的右孩子为结点2 * i + 2,否则结 点i无右孩子

堆支持以下的基本 :

  • HeapInit 建立一个堆 ;

  • HeapPush:  向堆中插入一个新元素 , 将新元素提升使其符合堆的性质 ;

  • HeapTop:获取当前堆顶元素的值 ;

  • HeapPush:删除堆顶元素,使删除堆顶元素的堆再次成为堆 ;

  • HeapDestory堆的销毁 ; 

堆的基本应用 : 

  • HeapSort : 利用堆的性质给数组排序 , 并且不创建堆  ( 利用之前的向上调整,将数组高造成一个大堆 , 然后数组的第一个元素                   是数组中的最大值将数组的第一个元素和最后一个元素交换 , 再将数组从第一个位置向下调整 , 这时数组的第一                     个元素又代表调整后数组中的最大值 , 再继续与后面的元素交换直到要调整的位置只剩下一个元素位置 , 数组排                     序结束 ( 记得没次更新要调整数组的大小 ) )
  • 海量数据top K问题  (只需要建一个有K个数据的小堆,每次取堆顶元素与海量数据比较,比堆顶元素的大 , 交换数据 ,并且向下                                    调整 , 若小于等于则不处理 , 所有数据比较完后 , 堆里边数据就是所要求的top K )

Heap.h

代码如下 : 

#pragma once
#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
typedef int HPDataType;

//堆的数据结构
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

//堆的初始化
void HeapInit(Heap* hp, HPDataType* a, int n);
//堆的销毁
void HeapDestory(Heap* hp);
//入堆
void HeapPush(Heap* hp, HPDataType x);
//出堆
void HeapPop(Heap* hp);
//取堆头元素
HPDataType HeapTop(Heap* hp);
//堆的大小
int HeapSize(Heap* hp);
//判断堆是否为空
int HeapEmpty(Heap* hp);
// 不要直接调Heap
void HeapSort(HPDataType* a, int n);
//测试
void TestHeap();

Heap.c

代码如下 : 

#include "Heap.h"
//数据交换
void Swap(HPDataType* child, HPDataType* parent)
{
	HPDataType ret;
	assert(child&&parent);
	ret = *child;
	*child =* parent;
	*parent = ret;
}
//向下调整
//当我们要建小堆时,只需要将向下(向上)调整中判断孩子代表的数值大于父亲代表的数值
//改为小于即可
void AdjustDown(HPDataType*a, int n, int place)
{
	int child = 0;
	int parent = 0;
	assert(a);
	parent = place;
        //计算出要调节数组中的孩子下标
	child = parent * 2 + 1;
	while (child < n)
	{
                //计算出孩子中的最大值
		if ((child + 1<n) && (a[child + 1]>a[child]))
			++child;
                //如果孩子的数值大于父亲的数值,则交换两者数值
                //并且更新孩子和父亲所代表的坐标,继续向下调整
		if (a[child]>a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
//向上调整
void AdjustUp(HPDataType *a, int place)
{
	int child = 0;
	int parent = 0;
	assert(a);
	child = place;
        //计算出要调节数组中的父亲下标
	parent = (child - 1) / 2;
	while (child > 0)
	{
                //如果孩子的数值大于父亲的数值,则交换两者数值
                //并且更新孩子和父亲所代表的坐标,继续向上调整
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}
//堆的初始化
void HeapInit(Heap* hp, HPDataType* a, int n)
{
	int parent = 0;
	int i = 0;
	assert(hp&&a);
	hp->_a = (HPDataType*)malloc(sizeof(HPDataType)*n);
	assert(hp->_a);
	hp->_capacity = n;
	hp->_size = 0;
	for (i = 0; i < n; i++)
	{
		hp->_a[i] = a[i];
		hp->_size++;
	}
	//根据堆的性质计算出最后一个父亲的数组下标,表示要调整的位置
        //从最后一个父亲下标开始调整,不断更新,直到为堆顶为止
	for (parent = (n - 2) / 2; parent >= 0; parent--)
		AdjustDown(hp->_a, n, parent);
}
//销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->_a);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
}
//入堆
//入堆时将要入堆的元素向上调整
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->_capacity = hp->_size + 1)
	{
		hp->_capacity *= 2;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType)*hp->_capacity);
		assert(hp->_a);
	}
	hp->_a[hp->_size++] = x;
	AdjustUp(hp->_a, hp->_size - 1);
}
//出堆
//将堆顶元素出堆之后,只需要将数组中的最后一个元素好堆顶元素交换
//然后删除数组的最后一个元素,再将新的堆顶元素向下调整即可
void HeapPop(Heap* hp)
{
	assert(hp);
	Swap(&hp->_a[0], &hp->_a[hp->_size- 1]);
	AdjustDown(hp->_a, --hp->_size-1, 0);
}
//取堆头元素
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	return hp->_a[0];
}
//堆的大小
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->_size;
}
//堆是否为空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->_a > 0 ? 1 : 0;
}
//打印
void HeapPrint(Heap* hp)
{
	int i = 0;
	assert(hp);
	for (i = 0; i < hp->_size; i++)
		printf("%d ", hp->_a[i]);
	printf("\n");
}
//将数组从小到大排序
// 不要直接调Heap
//利用之前的向上调整,将数组高造成一个大堆,然后数组的第一个元素就是数组中的最大值
//将数组的第一个元素和最后一个元素交换,再将数组从第一个位置向下调整,这时数组的第
//一个元素又代表调整后数组中的最大值,再继续与后面的元素交换直到要调整的位置只剩下
//一个元素位置,数组排序结束(记得没次更新要调整数组的大小)
void HeapSort(HPDataType* a, int n)
{
	int parent = 0;
	int size = n-1;
	int i = 0;
	assert(a);
	for (parent = (n - 2) / 2; parent >= 0; parent--)
		AdjustDown(a, n, parent);
	while (size > 0)
	{

		Swap(&a[0], &a[size--]);
		//size代表的要调整数组的大小
		AdjustDown(a,size+1,0);
	}
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void TestHeap()
{
	Heap hp;
	int n = 0;
	HPDataType a[] = {53, 17, 78, 9, 45, 65, 87, 23, 31};
	n = sizeof(a) / sizeof(a[0]);
	HeapInit(&hp, a, n);
	HeapPrint(&hp);
	HeapSort(a, n);
}

Test.c

代码如下 : 

#include "Heap.h"
int main()
{
	TestHeap();
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ds19980228/article/details/82023829
今日推荐