常见排序详解

1.插入排序

工作原理是通过构建有序序列,对未排序的数据逐个进行插入操作。具体步骤如下:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5。

详细操作视频: https://www.bilibili.com/video/BV1TD4y1Q751/?spm_id_from=333.337.search-card.all.click

插入排序的时间复杂度为O(n^2),最好情况: O(n) , 空间复杂度: O(1)

在实际应用中适用于数据量较小的情况。

插入排序代码如下:

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{

		//一次排序
		// [0,end] end+1   end是插入位置
		int end = i;
		int tem = a[end + 1];
		while (end >= 0)
		{
			if (tem < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
				break;
		}
		a[end + 1] = tem;
	}
	
}

2.选择排序

工作原理是每次从未排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾。具体步骤如下:

  1. 在未排序序列中找到最小(或最大)的元素,存放到排序序列的起始位置。
  2. 从剩余未排序元素中继续寻找最小(或最大)的元素,放到已排序序列的末尾。
  3. 重复步骤2,直到所有元素均被排序。

详细操作视频:  https://www.bilibili.com/video/BV1qS4y1w7jM/?spm_id_from=333.337.search-card.all.click&vd_source=de668597adca807833a3791e57356a02

选择排序的 时间复杂度为O(n^2),最好情况: O(n^2)   空间复杂度: O(1)

在实际应用中适用于数据量较小的情况。

选择排序代码如下:

void SelectSort(int* a, int n)
{
	int star = 0, end = n - 1;
	while (star < end)
	{
		int min = star, max = star;
		for (int i = star + 1; i <= end; i++)
		{
			if (a[i] < a[min])
			{
				min = i;
			}

			if (a[i] > a[max])
			{
				max = i;
			}
		}
		Swap(&a[star], &a[min]);
		if (max == star)
			max = min;
		Swap(&a[end], &a[max]);
		star++;
		end--;
	}
}

3.堆排序

基于完全二叉树的排序算法,它利用堆的数据结构来进行排序。堆是一种特殊的树形数据结构。

基本思想是首先将待排序的序列构建成一个最大堆(或最小堆),然后将堆顶元素与堆的最后一个元素交换并移出堆,然后对剩余的元素重新进行堆调整操作,重复这个过程直到整个序列有序。

详细操作视频: https://www.bilibili.com/video/BV1aj411M71h/?spm_id_from=333.337.search-card.all.click&vd_source=de668597adca807833a3791e57356a02

时间复杂度: O(n*logn)     最好情况:  O(n*logn)       空间复杂度: O(1)

适用于大数据量的排序。由于堆排序需要进行大量的元素交换和堆调整操作,因此它的性能相对较高。

堆排序代码如下:

//堆排序(不受输入数据的影响,而是由算法本身决定的)

void HeapSort(int* a, int n)
{
	for( int i = (n-1-1) / 2 ; i >= 0 ; i--)
	{
		AdjustDown( a , n ,i );
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

//向下调整
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	//找出左右孩子中值较小的一个,和parent比较,进而判断是否交换

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

}

4.快速排序

基本思想是选择一个基准元素,将序列分为两部分,一部分的元素都小于基准元素,另一部分的元素都大于基准元素,然后对这两部分分别进行快速排序,直到整个序列有序。

具体步骤如下:

  1. 选择一个基准元素,通常是序列中的第一个元素。
  2. 将序列中小于基准元素的元素移到基准元素的左边,大于基准元素的元素移到基准元素的右边。
  3. 对基准元素左右两部分分别进行递归的快速排序。

详细操作视频:  https://www.bilibili.com/video/BV18T4y197xL/?spm_id_from=333.337.search-card.all.click&vd_source=de668597adca807833a3791e57356a02

时间复杂度: O(n^2)    最好情况: O(n*logn)       空间复杂度: O(log n)

适用于大数据量的排序。由于快速排序采用了分治的思想,因此在实际应用中具有较高的性能。

快速排序代码如下:

void QuickSort(int* a, int start, int end)
{
	if (start >= end) {
		return;
	}

	if (end - start + 1 <= 10)
		InsertSort(a, end - start + 1);
	else
	{
		int mid = GetMid(a, start, end);
		Swap(&a[mid], &a[start]);

		int left = start, right = end;
		int key = start;
		while (left < right) {
			while (a[right] >= a[key] && left < right)
				--right;

			while (a[left] <= a[key] && left < right)
				++left;

			if (left < right)
				Swap(&a[left], &a[right]);
		}
		Swap(&a[key], &a[left]);
		key = left;
		QuickSort(a, start, key - 1);
		QuickSort(a, key + 1, end);
		
	}
	
}

5.归并排序

基本思想是将待排序序列分为若干个子序列,分别进行排序,然后将已排序的子序列合并成一个有序的序列。

具体步骤如下:

  1. 将待排序序列不断地二分,直到每个子序列只有一个元素。
  2. 将相邻的子序列两两合并,合并过程中保持元素有序。
  3. 重复步骤2,直到所有子序列合并成一个完整的有序序列。

详细操作视频:  https://www.bilibili.com/video/BV1hs4y1a7aM/?spm_id_from=333.337.search-card.all.click

时间复杂度: O(n*logn)       最好情况: O(n*logn)           空间复杂度: O(n)

归并排序采用了分治的思想,因此在实际应用中具有较高的性能。同时,归并排序也适用于外部排序,因为它对数据的访问方式是顺序访问,不会产生大量的随机访问,适合处理大规模数据。

快速排序代码如下:

//归并排序
void MergeSort(int* a, int n)
{
	int* tem = (int*)malloc(sizeof(int) * n);
	if (tem == NULL)
		return;
	_MergeSort(a, 0, n - 1, tem);
	free(tem);
}

void _MergeSort(int* a, int start, int end, int* temp)
{
	if (start >= end)
		return;
	int mid = (start + end) / 2;
	//分为区间: 
	// [start , mid] [mid + 1 ,end]
	_MergeSort(a, start, mid, temp);
	_MergeSort(a, mid + 1, end, temp);

	//归并
	int start1 = start, end1 = mid;
	int start2 = mid + 1, end2 = end;
	int i = start;

	while (start1 <= end1 && start2 <= end2)
	{
		if (a[start1] < a[start2])
			temp[i++] = a[start1++];
		else
			temp[i++] = a[start2++];
	}

	while (start1 <= end1)
		temp[i++] = a[start1++];
	while (start2 <= end2)
		temp[i++] = a[start2++];

	// 将合并后的结果拷贝回原数组
	memcpy(a + start, temp + start, sizeof(int) * (end - start + 1));
}

额外补充:

我们将排序分为稳定排序和不稳定排序:

稳定排序:两个元素值相同的元素排序后的先后位置不变

不稳定排序:两个元素值相同的元素排序后的先后位置改变

如下图中的序列中, A 指向的 5 在排序前的位置在 B 指向的 5 位置的前面,而排序后 A指向的5仍然在 B 指向的 5 前面,我们就称这种排序是 稳定排序; 相反 则是不稳定排序。

常见的稳定排序有:  插入排序 、 冒泡排序 、归并排序 、 基数排序

相反其他排序就是不稳定排序:  希尔排序 、选择排序 、堆排序 、快速排序 

以上就全部内容,感谢各位程序员能够看到最后!!!

希望大家多多支持,点点关注!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值