堆创建、插入、删除和排序

1.创建堆

堆:n个元素序列{k1,k2,...,ki,...,kn},当且仅当满足下列关系时称之为堆:

(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4,...,n/2)

若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

一般用数组来表示堆,i结点的父结点下标就为(i–1)/2。它的左右子结点下标分别为2*i+1和2*i+2。如第0个结点的左右子结点下标分别为1和2。

从无序序列建堆的过程就是一个反复调整的过程。若将此序列看成是一个完全二叉树,则最后一个非终端结点是第(n-2)/2个结点,由此调整过程只需从该结点开始,直到堆顶元素。

#ifndef _HEAP_H_
#define _HEAP_H_
#include
#include
#include
#include
#include
#define MAXSIZE  10
typedef int DataType;

typedef struct heap
{
	DataType* arr;
	int size;
	int capacity;
}heap,*pheap;



//堆的初始化
void HeapInit(pheap hp)
{
	hp->size = 0;
	for (int i = 0; i < 10; i++)
	{
		hp->arr[i] = rand()%10;
		hp->size++;
	}
	hp->capacity = MAXSIZE;
}


//建堆
void CreatHeap(pheap hp)
{
	int i;
	for (i = (hp->size - 2) / 2; i >= 0; i--)
	{
		AdjustDown(hp,i);
	}
}


//向下调整(建堆)
void AdjustDown(pheap hp,int parent)
{
	assert(hp);
	//确定第一个非叶子结点的左孩子
	int lchild = 2 * parent + 1;
	while (lchildsize)
	{//判断第一个非叶子结点是否有右孩子并且比较左右孩子的大小,取小的
		if (lchild+1< hp->size&&hp->arr[lchild]>hp->arr[lchild+1])
			lchild++;
		if (hp->arr[lchild] >= hp->arr[parent])
			break;
		DataType temp = hp->arr[parent];
		hp->arr[parent] = hp->arr[lchild];
		hp->arr[lchild] = temp;
		parent = lchild;
		lchild = 2 * parent + 1;
	}
}

2.堆的插入

每次插入都是将先将新数据放在数组最后,由于从这个新数据的父结点到根结点必然为一个有序的序列,现在的任务是将这个新数据插入到这个有序序列中——这就类似于直接插入排序中将一个数据并入到有序区间中。

//堆尾的插入
void HeapInsert(pheap hp, DataType data)
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		hp->arr = realloc(hp->arr, sizeof(DataType)*MAXSIZE * 2);
		hp->capacity = MAXSIZE * 2;
	}
	hp->arr[hp->size] = data;
	hp->size++;
	AdjustUp(hp);
}



//向上调整(插入)
void AdjustUp(pheap hp)
{//确定第一个非叶子结点
	int parent = (hp->size - 2) / 2;
	//确定第一个非叶子节点的左孩子
	int lchild = 2 * parent + 1;
	//一定要加上判断条件lchild!=0;如果在只有两个结点,且双亲结点
	//hp->arr[parent]>hp->arr[lchild]时,就会造成死循环。
	while (parent>=0&&lchild!=0)
	{//判断插入结点位置
		if (lchild + 1 < hp->size)
			lchild++;
		if (hp->arr[lchild] >= hp->arr[parent])
			break;
		DataType temp = hp->arr[parent];
		hp->arr[parent] = hp->arr[lchild];
		hp->arr[lchild] = temp;
		lchild = parent;
		parent = (lchild - 1) / 2;
	}
}

3.堆顶的删除

堆中每次都只能删除堆顶元素。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于根结点数据的“下沉”过程。

//堆顶的删除
void Heapdel(pheap hp)
{
	assert(hp);
	if (!hp->size)
		return;
	DataType temp = hp->arr[0];
	hp->arr[0] = hp->arr[hp->size - 1];
	hp->arr[hp->size - 1] = temp;
	hp->size--;
	AdjustDown(hp,0);
}

void AdjustDown(pheap hp,int parent)
{
	assert(hp);
	//确定第一个非叶子结点的左孩子
	int lchild = 2 * parent + 1;
	while (lchildsize)
	{//判断第一个非叶子结点是否有右孩子并且比较左右孩子的大小,取小的
		if (lchild+1< hp->size&&hp->arr[lchild]>hp->arr[lchild+1])
			lchild++;
		if (hp->arr[lchild] >= hp->arr[parent])
			break;
		DataType temp = hp->arr[parent];
		hp->arr[parent] = hp->arr[lchild];
		hp->arr[lchild] = temp;
		parent = lchild;
		lchild = 2 * parent + 1;
	}
}

4.堆排序

若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重建一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

输出堆顶元素之后,以堆中最后一个元素替代之,此时根结点的左右子树均为堆,则仅需进行一次从上到下的调整即可重建一个堆。


 堆排序算法。
 形参heap为大顶堆时,实现的是由小到大;
 形参heap为小顶堆时,实现的是由大到小;
 
void HeapSort(pheap hp)
{
	int i;
	int temp;
	int num = hp->size;
	while (hp->size > 1)
	{
		for (i = hp->size - 1; i > 0; i--)
		{//交换堆顶元素
			temp = hp->arr[0];
			hp->arr[0] = hp->arr[i];
			hp->arr[i] = temp;
        //改变堆调整的数据个数
			hp->size--;
			AdjustDown(hp, 0);
		}
	}
     printf("堆排序:");
	for (int i = 0; i < num; i++)
	{
		printf("%d ", hp->arr[i]);
	}
}

5.堆测试

int main()
{
	pheap hp;
	hp = (pheap)malloc(sizeof(heap));
	if (!hp)
		return NULL;
	hp->arr= (DataType*)malloc(sizeof(DataType) * MAXSIZE);
	if (!hp->arr)
		return NULL;
	//用数组来初始化堆
	HeapInit(hp);
	printf("用数组来初始化堆:");
	//打印数组
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->arr[i]);
	}
	printf("\n");
	//创建小顶堆并打印
	CreatHeap(hp);
	printf("创建小顶堆:");
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->arr[i]);
	}
	printf("\n");
	//堆插入数据 3
	HeapInsert(hp, 3);
	printf("堆插入数据 3:");
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->arr[i]);
	}
	printf("\n");
	//堆顶的删除
	Heapdel(hp);
	printf("堆顶的删除:");
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->arr[i]);
	}
	printf("\n");
	//堆排序
	HeapSort(hp);
	printf("\n");
	system("pause");
	return 0;
}

6.测试截图

堆创建、插入、删除和排序_第1张图片

你可能感兴趣的:(堆创建、插入、删除和排序)