堆排序、归并排序、分配排序与基数排序

今天发现其实我不是很会堆排序,重新整理记录一下。

堆排序:

将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值;重复执行,得到一个有序序列,这个过程叫堆排序。
围绕两个问题:
1.如何建堆
建堆是一个从下往上进行“筛选”的过程
在这里插入图片描述

2.如何调整元素在堆中的位置
第二个问题解决方法——筛选 (以堆中最大元素“沉降”)
输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”。

建堆代码:

template<typename E,typename Comp>
void heapsort(E A[],int n) {//heapsort
   E maxval;
   maxheap<E,Comp> H(A,n,n); 
   for(int i=0;i<n;i++) {  //now sort
   	maxval=H.removefirst();  //put max at end
   }
}

建堆图示:
在这里插入图片描述

堆排序

主要想强调一下代码。

template <typename E,typename Comp>
void mergesort() {
	if(left==right) return;  //终止条件为区间大小为1
	int mid=(left+right)/2;
	mergesort<E,Comp>(A,temp,left,mid);  //直接以mid为界递归
	mergesort<E,Comp>(A,temp,mid+1,right);
	for(it i=left;i<right;i++)  //把排好序的左右两部分copy到temp 
		temp[i]=A[i];
	//借助temp将左右两端排好序,放入A 
	int i1=left;int i2=mid+1;
	for(int curr=left;curr<=right;curr++) {
		if(i1=mid+1) A[curr]=temp[i2++];
		else if(i2>right) A[curr]=temp[i1++];
		else if(Comp::prior(temp[i1],temp[i2]))
			A[curr]=temp[i1++];
		else A[curr]=temp[i2++];
	}
}
分配排序

(只能对0到n-1个元素进行排序)
(可以适用于外排)
动机:按关键码划分,不进行关键码的比较
基本思想:根据关键码的位置确定一个记录在排序中的最终位置。

template <typename E,class getKey>
void binsort(E A[],int n) {
	List<E> B[MaxKeyValue];
	E item;
	for(int i=0;i<n;i++) B[A[i]].append(getKey(A[i]));
	//在B的第A[i]个桶中放入A[i]
	for(int i=0;i<MaxKeyValue;i++) 
		for(B[i].setStart();B[i].getValue(item);B[i].next())
		output(item);
	//检查每个盒子,输出,可能用了迭代器,这里看不懂 
}

桶式排序(和分配排序有相似之处)
很有分治法的味道在这里插入图片描述在这里插入图片描述因为分配时必须检查每个盒子中是否已有记录。
可以观察代码发现时间代价为O(n+MaxKeyValue)

基数排序:(是一种分配排序)
在这里插入图片描述在这里插入图片描述基本过程如图,第一趟对个位进行排序,按照个位数字丢入十个盒子中。
然后从里到外,从上到下把元素取下来。形成第一个序列。
在第二次排序中,按照十位的十个数字,将元素丢入十个盒子中。
然后从里到外,从上到下把元素取出来,得到第二个序列。本题中为最终的目标序列。
实现过程:
在这里插入图片描述代码:

//不得不说基数排序的代码非常巧妙,点个赞 
template <typename E,class getKey>
void radix(E A[],E B[],int n,int k,int r,int cnt[]) {
	//cnt是辅助数组 
	int j;
	for(int i=0,roti=1;i<k;i++,roti*=r) {  //总共要循环n位 
		for(j=0;j<r;j++) cnt[j]=0;  //初始化 
		for(j=0;j<n;j++) cnt[(getKey::key(A[j])/roti)%r]++;  
		//统计0~9号格子里各要扔多少元素 
		for(j=1;j<r;j++)
			cnt[j]=cnt[j-1]+cnt[j];
		//分别存储所有格子垒起来时,每个格子的下标位置 
		for(j=n-1;j>=0;j--) B[--cnt[getKey::key(A[j])/rtok%r]]=A[j];
		//从最后一个开始,把元素从后向前的放入每个格子 
		for(j=0;j<n;j++) A[j]=B[j];
		//一次循环结束 
	}
}

在这里插入图片描述综上,基数排序时有最好渐进复杂性的排序算法。astonishing

总结一下所有排序算法性能
时间性能

1.平均时间性能
时间复杂度位O(nlogn): 快速排序、堆排序和归并排序
时间复杂度为O(n2): 直接插入排序、冒泡排序和简单选择排序
时间复杂度O(n): 基数排序

2.当待排序记录列按关键字有序时
直接插入排序和冒泡排序能达到O(n)时间复杂度
快速排序的时间性能退化为O(n2) ,最能展现快速排序优势情况为,每次pivot的最终位置都正好位于字段的最终间

3.简单选择排序、堆排序和归并排序的时间性能不随记录序列中关键字的分布而改变

空间性能

指排序过程中所需辅助空间的大小
1.所有的简单排序方法(直接插入、冒泡、简单选择)和堆排序方法的空间复杂度O(1),这里可以说道说道,堆排序算法的顺序实现中,传入装有n个元素的数组,直接在其上进行时间复杂度为O(nlogn)的建堆和后续恢复就可以。所以空间复杂度认为是O(1).

2.快速排序为O(logn),为递归程序执行过程中,栈所需的辅助空间

3.归并排序所需辅助空间最多,复杂度为O(n)

本章讨论的各种排序方法,除基数排序外,其他方法都是基于“比较关键字”进行排序的排序方法。可以证明,这类排序法所能达到的最快时间复杂度为O(nlogn) 基数排序不在这个范畴内,所以不受限制

稳定与否

1.稳定的排序算法
插入排序、冒泡排序和归并排序、分配排序和基数排序
2.不稳定的排序算法
选择排序,堆排序、快速排序和shell排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值