排序算法整理(5)堆排序

程序参考了CLRS的《算法导论》,第六章 Heap Sort。

由于最大堆和最小堆是对称的,下文都说最大堆。


1 堆的定义

最大堆就是这样一种树,每个节点而言,如果它有儿子,那么此节点比它的儿子都大。

需要注意的有两点。

首先,堆几乎是一种完全二叉树,也可能是不完全的,下面第4部分中的图一。

其次,如果要用数组array来表示堆。那么必需两个元素,数组名和堆的大小heapsize。具体方式接下来解释。

第三,本文为了实现方便,忽略了array[0]。比如,如果要排序10个数,本文采取的方式是创建array[11],用array[1], array[2], … ,array[10]来表示这10个数。


 2 堆排序的基本方法

堆排序的方法是

step 1把输入的数组改造成一个最大堆(使用max_heapify( )函数)

step 2 交换堆顶(最大元素)和堆底的最后一个元素

step 3 堆的大小heapsize减一

step 4 对剩下的元素重复step1 ~ step3的过程


3 创建堆的核心函数max_heapify

max_heapify是进行堆的创建和进行堆排序的核心函数。

可以递归和非递归两种方式来进行实现。

先说递归的方式,

max_heapify这个函数的输入是数组,array,下标index。

使用这个函数的前提条件是,由array[index]的左子树和右子树都已经是最大堆了。

但是array[index]可能并不比它的左儿子大,或者并不比它的右儿子大。此时需要调整array[index]和它的左儿子,右儿子的位置,使得array[index]和它的左儿子节点,右儿子节点,这3个节点重组为最大堆,也就说,array[index]比左儿子和右儿子中最大的那个儿子要大。

经过这次调整以后,array[index]和array[index]的左儿子,array[index]的右儿子构成了最大堆。但是,观察array[index]的左子树,左子树可能并不是一个最大堆了。于是,对左子树进行调整,递归地进行刚才的调整过程。或者,右子树可能不是一个最大堆了,同理应该对右子树进行调整。

再说非递归的方式。

如果非递归的进行,则从root到叶节点的方向,依次地进行每个节点和它的两个儿子的比较,不断地进行当前节点和左儿子(或者右儿子,取决于谁大)的比较,直到到了叶节点为止。

递归实现的代码如下

void max_heapify_recur(int * p_arr,int i,int heap_size) 
{
	int l = l_child(i);
	int r = r_child(i);
	int largest = 0;
	if ( (l<= heap_size) && (p_arr[l]>p_arr[i]) )
	    largest = l;
	else
		largest = i;
	if( (r<=heap_size) && (p_arr[r]>p_arr[largest]) )
		largest = r;
//	printf("i:%d,largest:%d,array[i]:%d,array[largest]:%d\n",i,largest,p_arr[i],p_arr[largest]);
	if (largest != i) {
		int temp = p_arr[i];
	    p_arr[i] = p_arr[largest];
		p_arr[largest] = temp;
		max_heapify_recur(p_arr, largest,heap_size);
	}
	return;
}


非递归实现的代码如下

void max_heapify_norecur(int * p_arr,int i,int heap_size) 
{
	while(i<=heap_size/2) {	
		int l = l_child(i);
		int r = r_child(i);
		int largest = 0;
		if ( (l<= heap_size) && (p_arr[l]>p_arr[i]) )
			largest = l;
		else
			largest = i;
		if( (r<=heap_size) && (p_arr[r]>p_arr[largest]) )
			largest = r;
//		printf("i:%d,largest:%d,array[i]:%d,array[largest]:%d\n",i,largest,p_arr[i],p_arr[largest]);
		if (largest != i) {
			int temp = p_arr[i];
			p_arr[i] = p_arr[largest];
			p_arr[largest] = temp;		
			i=largest;
		}
	}
	return;
}



4 创建堆的完整实现

首先应用一个小结论,如果一个堆有n个节点(用数组array[1] ,array[2]…,array[n]来表示),那么这个堆的叶节点的下标是(int)n/2+1, (int)n/2+2…n 。(稍后另写一下对树相关的数量关系的总结)

如果要构造一个最大堆,则从下标最大的非叶节点开始,到下标最小的非叶节点为止(到根节点为止),逐步调用max_heapify即可。如下所示,偷来一张CLRS的配图。

即从index = (int) n/2开始,到index = 1为止,逐步调用max_heapify,

如下图,对10个元素进行排序,从index = 5开始,到index =1为止,逐步调用max_heapify。


                                                          图一

创建堆的代码如下

void build_heap(int * p_arr,int heapsize)
{
	int start=heapsize/2;
	for(int i = start;i>=1;i-- )
	{
		max_heapify_recur(p_arr,i,heapsize);
	}
	return;
}


5 堆排序的实现

假设待排序的有n个数,放在数组array[n+1]中,元素分别表示为array[1], array[2],…array[10],为了方便,跳过了array[0],从array[1]开始记录。

以n=10为例子来看。

对这array[1]这1个数,构造最大堆,显然根据定义和创建最大堆的过程,array[1]构成的最大堆也就是它自己了,最大节点在堆顶,为array[1],交换array[1]和array[10]

发生交换后,对剩下9个数进行堆排序,此时的最大元素依然在堆顶,依然为array[1],交换array[1]和array[9],

发生交换后,对剩下8个数进行堆排序,此时的最大元素依然在堆顶,依然为array[1],交换array[1]和array[8],

..

发生交换后,对剩下2个数进行堆排序,此时的最大元素依然为array[1],交换array[1]和array[2],

发生交换后,剩下1个数,没有排序的必要。

整理,输出数组。

代码如下

void heap_sort(int * p_arr, int length)
{
	int * adjusted_array =  new int[length+1];
	adjusted_array[0]=0;
	for(int i=0;i<length;i++) //构建下表从1开始的数组作为adjusted_array,方便进行堆排序
		adjusted_array[i+1]=p_arr[i];
	
	/*
	printf("测试1 排序前:\n");
	print_arr(adjusted_array,10);
	*/
	
	build_heap(adjusted_array,length);

	/*
	printf("测试2 创建heap后:\n");
	print_arr(adjusted_array,10);
	*/
	printf("\n\n");
	for(int heap_size=length;heap_size>=1;heap_size--) {		
		/*
		printf("heapsize is %d\n",heap_size);
		printf("测试3 交换前:\t");
		print_arr(adjusted_array,10);
		*/

		int temp = adjusted_array[1];
		adjusted_array[1] = adjusted_array[heap_size];
		adjusted_array[heap_size] = temp;	    
		max_heapify_recur(adjusted_array,1,heap_size-1);
		/*
		printf("测试4 交换后:\t");
		print_arr(adjusted_array,10);
		printf("\n");
		*/
	}

	for(int i=0;i<length;i++) //将adjusted_array还原为输入数组	
		p_arr[i]=adjusted_array[i+1];

	delete [] adjusted_array;
	return;
}


5 时间复杂度

max_heapify( )的时间复杂度为Olg(n) , 因为max_heapify( )的时间复杂度和堆的高度成线性关系,所以和lg(n)成线性关系。

创建堆的的时间负责度为O(n), CLRS《算法导论》第六章 上有证明,比较复杂,这里就不搬运了。

堆排序的的时间负责度为O(nlgn),n个元素,每次挑出当前的最大的1个元素(时间复杂度为常数),然后进行max_heapify( )操作(时间复杂度为lg(n)),所以时间复杂度为O(nlgn)。


6 堆数据结构的应用

下文, 排序算法整理(6)堆排序的应用,top K 问题 将讲讲堆这种数据结构的一个很奇妙的应用,top K问题,挑出一堆数中最大的K个数。



智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值