各种排序之插入、希尔、堆排序

插入排序算法

插入排序是最简单的排序算法。插入排序有N-1趟排序组成。对于P=1趟到P=N-1趟,插入排序保证从位置0到位置P上的元素已为排序状态。插入排序利用了这样的事实:位置0到位置P-1上的元素是排过序的。图1显示一个简单的数组在每一趟排序后的情况。

图1:每趟后的插入排序

如下代码实现了上述想法:

void InsertionSort(ElementType A[], int N)
{
	int j, p;
	ElementType Tmp;
	for (p = 1; p < N; ++p){
		Tmp = A[p];
		for (j = p; A[j - 1] > Tmp && j > 0; --j){
			A[j] = A[j - 1];
		}
		A[j] = Tmp;
	}
}

在第2行到第5行实现数据移动而没有明显使用交换。位置P上的元素存于Tmp,而在位置P之前所有更大的元素都被向右移动一个位置。然后Tmp被置于正确的位置上。

从代码中可以看出,插入排序复杂度是O(N^2),但如果输入数据已预先排序,那么运行时间为O(N)。

希尔排序

通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。所以希尔排序也叫缩小增量排序。希尔排序使用一个序列h1,h2,....,hn,叫做增量序列,只要h1=1,任何增量序列都是可以的,不过有些增量序列比另外一些增量序列更好。在使用增量hk的一趟排序后,对于每一个i,我们有A[i] <= A[i+hk],即所有相隔hk的元素都被排序,此时和排序文件是hk-排序的。希尔排序的一个重要性质是:一个hk-排序的文件保持它的hk-排序性。如果情况不是这样的话,希尔排序也就没有意义了。

hk-排序的一般做法是:对于hk,hk+1,...,N-1中的每一个位置i,把其上的元素放到i,i-hk,i-2*hk....中间的正确位置上。一趟hk排序的作用就是对hk个独立的子数组执行一次插入排序。

希尔排序的运行时间依赖于增量序列,但最坏情形下的运行时间为O(N^2),但对于有些增量序列,其时间可减少到O(N^1.2)。以下是以增量序列:1,2,4,N/2的一种实现:

void Shellsort(ElementType A[], int N)
{
	int i, j, Increment;
	ElementType Tmp;

	for (Increment = N / 2; Increment > 0; Increment /= 2){
		for (i = Increment; i < N; ++i){
			Tmp = A[i];
			for (j = i; j >= Increment; j -= Increment){
				if(Tmp < A[j-Increment])
					A[j] = A[j - Increment];
				else
					break;
			}
			A[j] = Tmp;
		}
	}
}

堆排序

堆排序主要是从二叉堆中通过删除最小元(树根)而得。回忆一下,建立N个元素的堆,需要线性O(N)的时间,然后执行N次DeleteMin操作。按照顺序,最小的元素先离开该堆。通过将这些元素离开的顺序即可得到N个元素的排序,因此总的时间复杂度是O(NlogN)。经验指出,堆排序是一个非常稳定的算法,它平均使用的比较只比最坏情形界指出的略少。执行堆排序的代码如下:

#define LeftChild(i) (2 * (i) + 1)

void PercDown(ElementType A[], int i, int N)
{
	int child;
	ElementType Tmp;

	for (Tmp = A[i]; LeftChild(i) < N; i = child){
		child = LeftChild(i);
		if (child != N - 1 && A[child + 1] > A[child])
			++child;				//找到最大的儿子节点
		if (Tmp < A[child])
			A[i] = A[child];
		else
			break;
	}
	A[i] = Tmp;
}

void HeapSort(ElementType A[], int N)
{
	int i;
	for (i = N / 2; i >= 0; --i)
		PercDown(A, i, N);			//构造堆
	for (i = N -1; i > 0; --i){
		Swap(&A[0], &A[i]);			//将最大元素(根)与数组末尾元素交换,从而删除最大元素,重新构造堆
		PercDown(A, 0, i);
	}
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值