数据结构之堆的定义、属性以及算法实现(C/C++描述)

目录

堆的定义 

最大堆

堆的一般规律

在数组中快速创建堆

堆的算法实现

最大堆中插入元素

堆顶元素出列


堆的定义 

堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。(来自百度百科)

最大堆

  1.  每个节点最多可以有两个节点
  2.  根节点的键值是所有堆节点键值中最大者,且每个父节点的值都比其左右孩子结点的值大
  3.  除了根节点、最后一个左子节点可以没有兄弟节点,其余节点必须有兄弟节点。如图

                                            

 堆的一般规律

  1.  i 的左子节点:2 * i + 1 ,i 的右子节点 2 * i + 2
  2.  i 的父节点:( i  - 1 ) / 2
  3.  最后一个父节点 size /2 -1   (注:size为总节点数,根节点从0开始计数

 在数组中快速创建堆

  1.  首先我们需要找到最后一个结点的父结点如图(a),我们找到的结点是 87,然后找出该结点的最大子节点与自
    己比较,若该子节点比自身大,则将两个结点交换.
    图(a)中,87 比左子节点 95 小,则交换之.如图(b)所示

  

2.  我们移动到前一个父结点 93,如图(c)所示.同理做第一步的比较操作,结果不需要交换

 3. 继续移动结点到前一个父结点 82,如图(d)所示,82 小于右子节点 95,则 82 与 95 交换,如图(e)所示,82 交换

后,其值小于左子节点,不符合最大堆的特点,故需要继续向下调整,如图(f)所示

 4. 所有节点交换完毕,最大堆构建完成

堆的算法实现

数据结构

#define DEFAULT_CAPACITY 128

typedef struct _Heap
{
	int *arr;//堆数组
	int size;//当前存储的元素个数
	int capacity;//当前存储的容量
}Heap;

建(最大)堆
/ 堆.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//Author:See QQ:3492625357 交流群:894356239
//代码为本人手写,若有错误或不当之处,欢迎指正。

#include <iostream>

#define DEFAULT_CAPACITY 128

typedef struct _Heap
{
	int *arr;//堆数组
	int size;//当前存储的元素个数
	int capacity;//当前存储的容量
}Heap;

bool initHeap(Heap &heap, int *orginal, int size); //堆的初始化
static void buildHeap(Heap &heap);
static void adjustDown(Heap &heap, int index);

int main()
{
	Heap hp;
	int origVals[] = { 1,2,3,87,93,82,92,86,95 };

	if (!initHeap(hp,origVals,sizeof(origVals)/sizeof(int)))
	{
		fprintf(stderr, "初始化堆失败!\n");
		return -1;
	}
	//输出最大堆 检测结果是否正确
	for (int j = 0; j < hp.size; j++)
	{
		std::cout << hp.arr[j] << " ";
	}
	std::cout << std::endl;
}

//堆的初始化
bool initHeap(Heap & heap, int * orginal, int size)
{
	int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : size;
	heap.arr = new int[capacity];
	if (!heap.arr) return false;

	heap.capacity = capacity;
	heap.size = 0;
	//如果存在原始数据则构建堆
	if (size>0)
	{
		memcpy(heap.arr,orginal,size * sizeof(int));
		heap.size = size;
		buildHeap(heap);
	}
	

	return true;
}

/* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节 点),
确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
void buildHeap(Heap & heap)
{
	
	for (int i = heap.size / 2 - 1; i >= 0; i--)  //堆的最后一个父节点是  size/2 -1
	{ 
		adjustDown(heap, i);
	}
}

 void adjustDown(Heap &heap, int index)
{
	 int lchild,rchild,maxchild;

	 for (int parent=index;(parent*2+1)<heap.size;parent=maxchild)
	 {
		 lchild = parent * 2 + 1;
		 rchild= parent * 2 + 2;
         maxchild = lchild;
		 //如果右子节点存在
		 if (rchild <heap.size)
		 {
			 //取最大子节点
			 maxchild = heap.arr[lchild] >= heap.arr[rchild] ? lchild : rchild;
		 }
		 //如果父节点小于子节点则交换
		 if (heap.arr[parent] <heap.arr[maxchild])
		 {
			 int tmp = heap.arr[parent];
			 heap.arr[parent] = heap.arr[maxchild];
			 heap.arr[maxchild] = tmp;
		 }
	 }
}

最大堆中插入元素

将数字 99 插入到上面大顶堆中的过程如下:
1. 原始的堆,如图 a ,对应的数组:{95, 93, 87, 92, 86, 82} 
2. 将新进的元素插入到大顶堆的尾部,如下图 b 所示,对应的数组:{95, 93, 87, 92, 86, 82, 99}

                       

3. 此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c

所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进
行交换,如图 d, 至此,最大堆调整完成。

 

算法实现:

 bool insertHeap(Heap &heap, int e)
 {
	 if (heap.size == heap.capacity) return false;

	 int index = heap.size;
	 heap.arr[heap.size++] = e;
	 adjustUp(heap, index);
 }

 /*将当前节点和父节点调整成最大堆*/
 static void adjustUp(Heap &heap, int index)
 {
	 if (index < 0 || index >= heap.size) return;

	 while (index>0)
	 {
		
		 int parent = (index - 1) / 2;
		 if (parent>=0)
		 {
			
			 if (heap.arr[parent]<heap.arr[index])
			 {
				 int temp = heap.arr[index];
				 heap.arr[index] = heap.arr[parent];
				 heap.arr[parent] = temp;
				
				 index = parent;
			 }
			 else
			 {
				 //如果父节点比当前节点大 则无须继续变更下去
				 break;
			 }
		 }
		 else
		 {
              //如果当前节点已经比根节点小或者等于跟节点则无须继续
			 break;
		 }
	 }
 }

 堆顶元素出列

当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元
素,将它放到堆的顶部,然后再修复堆属性。

算法实现

 static bool popMaxHeap(Heap &heap, int &e)
 {
	 if (heap.size < 1) return false;

	 e = heap.arr[0];
	 heap.arr[0] = heap.arr[--heap.size];

	 adjustDown(heap, 0);

 }

所有操作代码整合如下↓

//Author:See QQ:3492625357 交流群:894356239
//代码为本人手写,若有错误或不当之处,欢迎指正。

#include <iostream>

#define DEFAULT_CAPACITY 128

typedef struct _Heap
{
	int *arr;//堆数组
	int size;//当前存储的元素个数
	int capacity;//当前存储的容量
}Heap;

bool initHeap(Heap &heap, int *orginal, int size); //堆的初始化
bool insertHeap(Heap &heap, int e);
static void buildHeap(Heap &heap);
static bool popMaxHeap(Heap &heap, int &e);
static void adjustDown(Heap &heap, int index);
static void adjustUp(Heap &heap, int index);

int main()
{
	Heap hp;
	int origVals[] = { 1,2,3,87,93,82,92,86,95 };

	if (!initHeap(hp,origVals,sizeof(origVals)/sizeof(int)))
	{
		fprintf(stderr, "初始化堆失败!\n");
		return -1;
	}
	//输出最大堆 检测结果是否正确
	for (int j = 0; j < hp.size; j++)
	{
		std::cout << hp.arr[j] << " ";
	}
	std::cout << std::endl;

	insertHeap(hp, 100);
	std::cout << "插入元素 100 后:" << std::endl;
	for (int j = 0; j < hp.size; j++)
	{
		std::cout << hp.arr[j] << " ";
	}
	std::cout << std::endl;

	int value;
	popMaxHeap(hp, value);
	std::cout << "插入元素" << value <<" 后:" << std::endl;
	for (int j = 0; j < hp.size; j++)
	{
		std::cout << hp.arr[j] << " ";
	}
	std::cout << std::endl;
}

//堆的初始化
bool initHeap(Heap & heap, int * orginal, int size)
{
	int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : size;
	heap.arr = new int[capacity];
	if (!heap.arr) return false;

	heap.capacity = capacity;
	heap.size = 0;
	//如果存在原始数据则构建堆
	if (size>0)
	{
		memcpy(heap.arr,orginal,size * sizeof(int));
		heap.size = size;
		buildHeap(heap);
	}
	

	return true;
}

/* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节 点),
确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
void buildHeap(Heap & heap)
{
	
	for (int i = heap.size / 2 - 1; i >= 0; i--)  //堆的最后一个父节点是  size/2 -1
	{ 
		adjustDown(heap, i);
	}
}

 void adjustDown(Heap &heap, int index)
{
	 int lchild,rchild,maxchild;

	 for (int parent=index;(parent*2+1)<heap.size;parent=maxchild)
	 {
		 lchild = parent * 2 + 1;
		 rchild= parent * 2 + 2;
         maxchild = lchild;
		 //如果右子节点存在
		 if (rchild <heap.size)
		 {
			 //取最大子节点
			 maxchild = heap.arr[lchild] >= heap.arr[rchild] ? lchild : rchild;
		 }
		 //如果父节点小于子节点则交换
		 if (heap.arr[parent] <heap.arr[maxchild])
		 {
			 int tmp = heap.arr[parent];
			 heap.arr[parent] = heap.arr[maxchild];
			 heap.arr[maxchild] = tmp;
		 }
	 }
}

 bool insertHeap(Heap &heap, int e)
 {
	 if (heap.size == heap.capacity) return false;

	 int index = heap.size;
	 heap.arr[heap.size++] = e;
	 adjustUp(heap, index);
 }

 static bool popMaxHeap(Heap &heap, int &e)
 {
	 if (heap.size < 1) return false;

	 e = heap.arr[0];
	 heap.arr[0] = heap.arr[--heap.size];

	 adjustDown(heap, 0);

 }

 /*将当前节点和父节点调整成最大堆*/
 static void adjustUp(Heap &heap, int index)
 {
	 if (index < 0 || index >= heap.size) return;

	 while (index>0)
	 {
		
		 int parent = (index - 1) / 2;
		 if (parent>=0)
		 {
			
			 if (heap.arr[parent]<heap.arr[index])
			 {
				 int temp = heap.arr[index];
				 heap.arr[index] = heap.arr[parent];
				 heap.arr[parent] = temp;
				
				 index = parent;
			 }
			 else
			 {
				 //如果父节点比当前节点大 则无须继续变更下去
				 break;
			 }
		 }
		 else
		 {
              //如果当前节点已经比根节点小或者等于跟节点则无须继续
			 break;
		 }
	 }
 }

猜你喜欢

转载自blog.csdn.net/weixin_40582034/article/details/118970757
今日推荐