数据结构——堆

    • 1.堆的概念
    • 2.堆的实现
      • 2.1堆的向下调整算法
      • 2.2堆的构建
        • 2.2.1构造最小堆
        • 2.2.2时间复杂度分析:
      • 2.3堆的插入
      • 2.4 堆的删除,取堆顶元素,取堆的数据个数,堆的判空
    • 3.堆排序
      • 3.1 (小堆)降序

1.堆的概念

1、堆是一颗完全二叉树(适合使用顺序结构存储);

2、堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。

3、堆中每个结点的子树都是堆树。

数据结构——堆_第1张图片
数据结构——堆_第2张图片

2.堆的实现

2.1堆的向下调整算法

我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆(大堆也可以,这里用小堆举例)。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

void Swap(HPDataType* p, HPDataType* q)
{
     
	HPDataType temp = *p;
	*p = *q;
	*q = temp;
}
//前提:左右子树都是小堆
void AdjustDown(HPDataType* a, int n, int root)
{
     
	//找出左右孩子出小的一个
	int parent = root;
	int child = parent * 2 + 1;//左孩子

	while (child < n)
	{
     
		//找出左右孩子中小的一个
		if ((child + 1 < n) && a[child + 1] < a[child])
		{
     
			child = child + 1;
		}

		//如果孩子小于父亲就交换
		if (a[child] < a[parent])
		{
     
			Swap(&a[child], &a[parent]);
			parent = child;//继续下调
			child = parent * 2 + 1;
		}
		else
		{
     
			break;
		}
	}
	
}

2.2堆的构建

堆的数据结构如下:

typedef int HPDataType;
typedef struct Heap
{
      
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

2.2.1构造最小堆

基本思想:首先将每个叶子结点视为一个堆,再将每个叶子结点于其父节点一起构成一个包含更多结点的堆。所以在构造堆的时候,首先需要找到最后一个结点的父节点,从这个节点开始构造小堆,直到该节点前面的所有分支节点都处理完毕。

设当前元素在数组中以R[i]表示,那么:
1.它的左孩子结点是:R[2i+1];
2.它的右孩子结点是: R[2
i+2];
3. 它的父结点是:R[(i-1)/2];

堆的初始化:

//初始化
void HeapInit(Heap* php, HPDataType* arr, int n)
{
     
	php->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	memcpy(php->_a, arr, sizeof(HPDataType) * n);
	php->_size = n;
	php->_capacity = n;

	//构建堆
	//i是从最后一个非叶子结点的索引开始的
	for (int i = (n-1-1) / 2; i >= 0; i--)
	{
     
		AdjustDown(php->_a, n, i);//下调
	}
}

2.2.2时间复杂度分析:

第一层节点的个数为2^0个,单个节点向下调整的次数为h-1次
第二层的节点个数为2^1个,单个节点向下调整的次数为h-2次
第三层的节点个数为2^2个,单个节点的向下调整次数为h-3次

第h-1层的节点个数为2^(h-2)个,每个节点的向下调整次数为1次

即可求出:时间复杂度=2^0 * h-1+2^1 * (h-2)+2^2 * (h-3)+…+2^(h-2) * 1

通过h=logN和错位相减得到:时间复杂度=N-logN

即建堆的时间复杂度为:O(N)

2.3堆的插入

堆的插入是在最后一个位置插入,就是数组的尾插,然后在进行向上调整。

//向上调整
void AdjustUp(HPDataType* a, int n, int child)
{
     
	int 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 HeapPush(Heap* php, HPDataType x)
{
     
	assert(php);
	//如果空间不够增容
	if (php->_size == php->_capacity)
	{
     
		php->_capacity *= 2;
		HPDataType* temp = (HPDataType*)realloc(php->_a, sizeof(HPDataType) * php->_capacity);

		php->_a = temp;
	}

	php->_a[php->_size++] = x;//赋值,size+1

	AdjustUp(php->_a, php->_size, php->_size - 1);//传入
}

2.4 堆的删除,取堆顶元素,取堆的数据个数,堆的判空

堆的删除是删头

//删头
void HeapPop(Heap* php)
{
     
	assert(php);
	assert(php->_size > 0);

	Swap(&php->_a[0], &php->_a[php->_size - 1]);
	php->_size--;

	AdjustDown(php->_a, php->_size, 0);
}

//取堆顶的元素
HPDataType HeapTop(Heap* php)
{
     
	assert(php);
	assert(php->_size > 0);

	return php->_a[0];
}

// 堆的数据个数
int HeapSize(Heap* php)
{
     
	assert(php);
	return php->_size;
}

// 堆的判空 1为空,0为非空
int HeapEmpty(Heap* php)
{
     
	assert(php);
	return php->_size == 0 ? 1 : 0;
}

3.堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:
即子结点的键值或索引总是小于(或者大于)它的父节点。
堆排序的平均时间复杂度为 Ο(nlogn)

3.1 (小堆)降序

排降序用小堆

void HeapSort(int* a, int n)
{
     
	//1.排降序建小堆   2.排升序建大堆

	//1.建堆,i为每个小堆的root
	//时间复杂度O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
     
		AdjustDown(a, n, i);
	}
	
	//2.堆顶元素和最后一个元素互换,在对end-1个元素下调
	int end = n - 1;
	while (end > 0)
	{
     
		Swap(&a[0], &a[end]);//首尾互换,把最小的放到最后

		//再继续选次小的
		AdjustDown(a, end, 0);
		--end;
	}
}

你可能感兴趣的:(c,数据结构,二叉树,堆排序)