选择排序、冒泡排序----C语言数据结构

目录

    • 引言
  • 1.选择排序的实现
  • 1.1选择排序的时间复杂度
  • 2.冒泡排序的实现
  • 2.1冒泡排序的时间复杂度分析及优缺

引言

选择排序(Selection Sort)是一种简单直观的排序算法,它的基本思想是每次从未排序的元素中选择最小(或最大)的元素,然后将其放置在已排序部分的末尾。这个过程不断重复,直到所有元素都被排序完成。

选择排序虽然在时间复杂度上不如一些高级的排序算法,但由于其简单直观的实现方式,以及在某些特定情况下的一些优势:

  1. 小型数据集:选择排序在处理小规模数据集时可能比较适用。由于其基本操作是选择最小元素并交换,对于较小规模的数据集,选择排序可能不会带来很大的性能损失,并且实现较为简单。
  2. 内存有限的环境:
    由于选择排序是一种原地排序算法,它不需要额外的存储空间,仅使用常数级别的额外空间。在内存有限的环境下,选择排序可能是一个可行的选择,因为它不会占用太多内存。

1.选择排序的实现

基本思想: 每一次从待排序的数据元素中选出最小(或者最大的)的一个元素,置于最前端也就是序列的起始位置,使最前端的数据元素是有序的,直到全部待排序的数据元素全都排列完全

思路:

  1. 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
  2. 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  3. 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,继续此操作
    选择排序、冒泡排序----C语言数据结构_第1张图片

初始版

void Swap(int* a, int* b)
{
	int c = *a;
	*a = *b;
	*b = c;
}

void SelectSort(int* a, int n)
{
	for (int j = 0;j < n;j++)
	{
	//注意这里min记录的是下标而不是值,原因是记录值,无法实现交换
		int begin = j, min = begin;
		for (int i = j;i < n;i++)
		{
			if (a[i] <= a[min])
			{
				min = i;
			}
		}
		Swap(&a[begin], &a[min]);
	}
}

优化版
在实践中我们其实是可以再一趟循环中,同时交换最大值和最小值,也就是将最小值放到最前面,将最大值放到最后面,来提升效率

void SelectSort(int* a, int n)
{
	int begin = 0,end = n;
	while (begin<end)
	{
		//记录一趟循环的最大最小值
		int min = begin, max = begin;
		for (int i = begin + 1;i < end;i++)
		{
			if (a[i] <= a[min])
				min = i;
			if (a[i] > a[max])
				max = i;
		}
		Swap(&a[begin], &a[min]);
		//防止最大值为begin而被交换
		if (max == begin)
		{
			max = min;
		}
		Swap(&a[end - 1], &a[max]);
		begin++;
		end--;
	}
}

1.1选择排序的时间复杂度

时间复杂度是O(n^2)

  1. 最好情况时间复杂度:
    在最好的情况下,即输入数组已经是有序的状态,选择排序每次只需要比较一次元素就能确定最小值,然后进行一次交换。因此,最好情况时间复杂度为 O(n^2)。

  2. 最坏情况时间复杂度:
    在最坏的情况下,即输入数组是逆序的状态,选择排序每次都需要比较未排序部分的所有元素,找到最小值,然后进行交换。对于长度为 n 的数组,第一次需要比较 n 次,第二次需要比较 n-1 次,以此类推,总的比较次数是 n + (n-1) + (n-2) + … + 1,可以用等差数列求和公式求解,最坏情况时间复杂度为 O(n^2)。

  3. 平均情况时间复杂度:
    在平均情况下,选择排序的性能也是 O(n^ 2)。这是因为无论初始数组的顺序如何,每次选择都需要进行一次完整的未排序部分的遍历,找到最小值进行交换。平均情况下,比较次数和交换次数都是 O(n^2)。

  1. 简单直观: 选择排序是一种非常简单直观的排序算法,易于理解和实现。这使得它成为学习排序算法的良好入门例子。

  2. 原地排序: 选择排序是一种原地排序算法,只需要常数级别的额外空间。

  3. 不受输入数据分布影响:
    与某些排序算法(例如快速排序)不同,选择排序的性能不会受输入数据的分布情况的影响。它在任何情况下都需要相同数量的比较和交换操作。

  1. 低效率: 选择排序的时间复杂度为
    O(n^2),无论输入数据的分布如何,它都需要进行相同数量的比较和交换操作。在大规模数据集上,性能相对较差。

  2. 不稳定性: 选择排序是一种不稳定的排序算法,相等元素的相对顺序在排序后可能发生改变。

  3. 固定元素位置:选择排序每次都会选择最小的元素放置到正确的位置,这可能导致一些已经有序的元素在排序过程中仍然会发生交换,增加了不必要的操作。

  4. 不适用于部分有序数组: 对于部分有序的数组,选择排序的性能也较差,因为它在每一轮都会进行完整的比较和交换操作。

2.冒泡排序的实现

冒泡排序是比较基础的排序算法之一,其思想是相邻的元素两两比较,较大的数下沉,较小的数冒起来,这样一趟比较下来,最大(小)值就会排列在一端。整个过程如同气泡冒起,因此被称作冒泡排序。
  冒泡排序的步骤是比较固定的:

  1. 俩俩相邻元素比较,大的往后沉 (也就是交换),小的往前冒[单趟,内层循环]
  2. 按待排序数组的元素,依次重复遍历(外层循环),直到遍历整个数组

选择排序、冒泡排序----C语言数据结构_第2张图片

void Swap(int* a, int* b)
{
	int c = *a;
	*a = *b;
	*b = c;
}


//冒泡排序,把最大的排最后,排除最大的后的数组再遍历排最大
void BubbleSort(int* a, int n)
{	for(int j=0;j<n-1;j++)
	{
		for (int i = 0;i <n-j-1;i++)
		{
			if (a[i] > a[i + 1])
			{
				Swap(&a[i], &a[i + 1]);
			}
		}
	}
}

2.1冒泡排序的时间复杂度分析及优缺

时间复杂度是O(n^2)

最优:待排序数组是有序的,此时,内层的循环不用交换,只有外层循环遍历,所以时间复杂度为O(n^2)

最差:待排序数组是逆序的,此时,内层的循环需要完全遍历,交换。最坏情况时间复杂度为 O(n^2)。

平均:在平均情况下,冒泡排序的性能也是 O(n^2)。虽然在最好情况下只需要一次遍历,但平均情况下需要进行多次遍历,每次遍历的比较和交换次数都会影响总体的时间复杂度。

  1. 简单易理解: 冒泡排序是一种直观、容易理解的排序算法,适用于初学者学习排序算法的入门。
  2. 稳定性: 冒泡排序是一种稳定的排序算法,相等元素的相对顺序在排序后不会改变。
  3. 原地排序: 冒泡排序是一种原地排序算法,只需要常数级别的额外空间。

缺:

  1. 低效率: 冒泡排序的平均和最坏情况时间复杂度均为 O(n^2),性能相对较差,尤其在大规模数据集上。

  2. 不适合大规模数据: 由于其较高的时间复杂度,冒泡排序在处理大规模数据时效率较低,不如一些高级排序算法。

  3. 交换次数较多: 冒泡排序的主要操作是相邻元素的比较和交换,尤其在逆序的情况下,交换次数较多。

  4. 不适用于部分有序数组: 对于部分有序的数组,冒泡排序的性能也较差,因为它仍然需要进行大量的比较和交换操作。

你可能感兴趣的:(数据结构,c语言,数据结构,开发语言)