排序算法(一)

本文详细介绍了插入排序(包括直接插入排序和希尔排序)、选择排序(简单选择排序和堆排序)的基本思想、代码实现以及它们的时间复杂度和空间复杂度。重点讨论了排序算法的稳定性问题。
摘要由CSDN通过智能技术生成

目录

一、插入排序

1.直接插入排序

(1)基本思想

(2)代码实现(升序)

(3)分析

2.希尔排序

(1)基本思想

(2)代码实现(升序)

(3)分析

二、选择排序

基本思想

1.简单选择排序

(1)代码实现(升序)

(2)分析

2.堆排序

(1)代码实现(升序)

(2)分析


排序是我们在程序中经常用到的算法之一,接下来的两篇博客就将对常见的排序算法进行总结、实现、评价。

在此之前,先补充一个概念——

稳定性:如果排序后原来关键字相同的元素的相对顺序不变,则称此排序算法是稳定的,否则不稳定。

一、插入排序

1.直接插入排序

(1)基本思想

把待排序的元素按照关键值的大小依次插入到一个已经有序的有序序列中,直到所有元素都插入完毕即可得到该组元素的有序序列。

(2)代码实现(升序)

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])  //
			{
				a[end + 1] = a[end];
				end--;
			}
			else
				break;
		}
		a[end + 1] = tmp;
	}

}

(3)分析

每次插入元素时,所需移动次数最少为0,最多为已经有序的元素个数(从0到n-1),因此该算法的时间复杂度为O\left ( n^{2} \right )

不需要额外的空间,空间复杂度为O\left ( 1 \right )

从上面代码可以看出,在进行大小比较时,如果相等,也会退出循环,不再继续移动元素,保证了二者的相对次序不变,因此该算法稳定。(当然,如果把<改为<=,这样算法依然可以实现排序功能,但是不稳定了。尽管如此,我们在实现该算法时还是可以让其成为稳定的,因此我们认为该算法是稳定的。下面的其他稳定性的算法与此同理。从中也可以看出,我们更希望一个排序算法是稳定的。)

元素集合越接近有序,直接插入排序的时间效率就越高。

2.希尔排序

(1)基本思想

选取一个整数gap,把所有元素分成gap组:相距gap的元素为一组,并对每组元素都进行直接插入排序。然后逐步缩小gap,重复上述操作,直到gap=1时,进行直接插入排序后便可得到有序序列。(如有疑问,请看分析部分)

(2)代码实现(升序)

关于gap的取法有很多种,本代码gap从n开始逐渐减半,直到gap=1结束。(不论gap怎么取,最终的gap必须等于1)

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap /= 2;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])  //
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
					break;
			}
			a[end + gap] = tmp;
		}
	}
}

(3)分析

既然最后一次也要全部进行直接插入排序,为什么还要提前进行多次的分组插入排序呢?直接插入排序的时间复杂度为O\left ( n^2 \right ),而且元素越无序,越接近n^2,元素越有序,越接近1。提前进行多次分组插入排序可以让元素的排列逐渐趋向有序,这样时间效率会越来越高,因此希尔排序的时间复杂度也会比直接插入排序小。可以说,希尔排序是对直接插入排序的优化。

那么希尔排序的时间复杂度是多少呢?这是一个复杂的问题,因为它的时间是所取距离gap的函数,这涉及一些数学上尚未解决的难题,经过大量的实验,目前认为希尔排序的时间复杂度约为O\left ( n^{1.3} \right )

不需要额外空间,空间复杂度为O\left ( 1 \right )

在分组插入排序的过程中,相同的元素可能分到不同的组别,进而导致各自排序后其相对次序可能改变,因此希尔排序不稳定。

二、选择排序

基本思想

每次从待排序的数据元素中挑选出最小或最大的一个元素,存放在序列的起始或末端位置,直到所有元素有序为止。

1.简单选择排序

该算法应该是C语言入门阶段就应该了解熟知的排序算法,不再过多介绍。

(1)代码实现(升序)

void Swap(int* x, int* y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
}

void SelectSort(int* a, int n)
{
	for (int i = 0; i < n-1; i++)
	{
		int mini = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] < a[mini])  //
				mini = j;
		}
		if (mini != i)
			Swap(a + i, a + mini);
	}
}

(2)分析

时间复杂度为O\left ( n^2 \right )

空间复杂度为O\left ( 1 \right )

该算法不稳定。

简单选择排序效率太低,实际中很少使用。

2.堆排序

堆排序是通过建堆来挑选最大或最小的元素的。升序建大堆,降序建小堆。有关堆的细节可以查看上篇博客【堆的实现及应用】

(1)代码实现(升序)

void Swap(int* x, int* y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
}

void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])  //
		{
			child++;
		}
		if (a[child] > a[parent])  //
		{
			Swap(a + child, a + parent);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}


void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	int index_end = n - 1;
	while (index_end > 0)
	{
		Swap(a, a + index_end);
		AdjustDown(a, index_end, 0);
		index_end--;
	}
}

(2)分析

单次向下调整的时间复杂度为O(n),堆排序的时间复杂度为O\left ( n*log_{2}n \right )

不需要额外空间,空间复杂度为O\left ( 1 \right )

在建堆过程中相同的元素其相对次序会改变,堆排序不稳定

  • 21
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值