选择排序和归并排序

选择排序

选择排序的基本思想:每一次在n-i+1(i=1,2,…,n-1)个记录中选取键值最小的记录作为有序序列的第i个记录。

直接选择排序

直接选择排序算法的基本思想:在第i次选择操作中,通过n-i次键值间比较,从n-i+1个记录中选出键值最小的记录,并和第i(1≤i≤n-1)个记录交换。

算法描述如下:

void selectSort(List R,int n)
{
	int min,i,j;
	for(i=1;i<=n-1;i++)
	{
		min=i;
		for(j=i+1;j<=n;j++)
		{
			if(R[j].key<R[min].key)
			{
				min=j;
			}
			if(min !=i)
			{
				swap(R[min],R[i]);
			}
		}
	}
}

直接选择排序算法简单,容易实现,但不适宜于n较大的情况。时间复杂度为O(n²)。

堆排序

若有一个关键字序列{k1,k2…,kn} 满足 k(i)≤k(2i),k(i)≤k(2i+1)或k(i)≥k(2i),k(i)≥k(2i+1),其中,i=1,2,…, ⌊n/2⌋,则称这个n个键值的序列{k(1),k(2),…k(n)}为最小堆(或最大堆)。

算法如下:

void HeapSort(List R)
//对R[n]进行堆排序,排序完成后,R中记录按关键字自大至小有序排列
{
	int i;
	for(i=n/2;i>=1;i--)	
	{
		Shit(R,i,n);	//从第n/2个记录开始进行筛选建堆
	}
	for(i=n;i>=2;i--)
	{
		swap(R[1],R[i]);	//将堆顶记录和堆中最后一个记录互换
		Sift(R,1,i-1);		//调整R[1]使R[1],…,R[i-1]变成堆
	}
}

void Sift(List R,int k,int m)
{
	int i,j,x;
	List t;
	i=k;j=2*i;
	x=R[k].key;
	t=R[k];
	while(j<=m)
	{
		if((j<m) && (R[j].key>R[j+1].key))
		{
			j++;	//若存在右子树,且右字树根的关键字小,则沿右分支筛选
		}
		if(x<R[j].key)
		{
			break;		//筛选完毕
		}
		else
		{
			R[i]=R[j];
			i=j;
			j=2*i;
		}
	}
	R[i]=t;		//填入恰当位置
}

堆排序在待排序记录较少时不适用,但对记录数很多时是很有效的,因为其主要运行时间耗费在建初始堆和不断“筛选”的过程。对于n个记录进行排序所需的平均时间时O(nlog(2)n)。在最坏情况下,其时间复杂度也为O(nlog(2)n)。相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需一个记录大小的供交换用的辅助存储空间。

堆排序是不稳定的。

归并排序

归并排序是与插入排序、交换排序,选择排序不同的一类排序方法,其不同之处在于要求待排序列是由若干个子序列组成。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。合并的方法是比较各子序列的第一个记录的键值,最小的一个就是排序后序列的第一个记录的键值。取出这个记录,继续比较各自序列现有的第一个记录的键值,便可中出排序后的第二个记录。如此继续下去,最终可以得到排序结果。因此归并排序的基础是合并。

有序序列的合并

void Merge(List a,List R,int h,int m,int n)
//将ah,…am和am+1,…,an两个有序序列合并成一个有序序列Rh,…Rn
{
	k=h,j=m+1;		//k,j置成文件的起始位置
	while((h<=m) && (j<=n))	//将a中记录从小到大合并入R
	{
		if(a[h].key<=a[j].key)	//a[h]键值小,送入R[k]并修改h值
		{
			R[k]=a[h];
			h++;
		}
		else			//a[j]键值小,送入[k]并修改j值
		{
			R[k]=a[j];
			j++;
		}
		k++;
	}
	while(h<=m)		//j>n,将ah,…am剩余部分插入R的末尾
	{
		R[k]=a[h];
		h++;
		k++;
	}
	while(j<=n)		//h>m,将am+1,…,an剩余部分插入R的末尾
	{
		R[k]=a[j];
		j++;
		k++;
	}
}

此算法的执行事件为O(n-h+1)。

二路归并排序

void MergePass(List a,List b,int n,int h)
{
	i=1;
	while(i<=n-2*h+1)
	{
		Merge(a,b,i+h-1,i+2*h-1);	
		i+=2*h;
	}
	if(i+h-1<n)
	{
		Merge(a,b,i,I=h-1,n);
	}
	else
	{
		for(t=i;t<=n;t++)
		b[t]=a[t];
	}
}
void MergeSort(List a,int n)	//将序列a1,a2,…an按关键字的非递减次序进行排序,b也定义为list类型
{
	m=1;	//m为子序列长度,初始值为1
	while(m<n)	
	{
		MergePass(a,b,n,m);	//将序列a中有序子序列合并到b
		m=2*m;		//子序列长度扩大1倍
		MergePass(b,a,n,m);	//将序列b中有序子序列合并到a
		m=2*m;		//子序列长度扩大1倍
	}
}

在n较大时,归并排序的时间性能优于堆排序,但它所需的辅助存储量较多。二路归并排序是稳定的。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值